代码改变世界

自定义ORM系列(一)利用attribute实现简单的reader=>entity和reader=>List<entity>映射

2010-07-09 15:27  Virus-BeautyCode  阅读(3625)  评论(7编辑  收藏

  我不知道NH的ORM具体如何实现的,我的想法就是通过字段名称和属性名称的对应关系来实现赋值。

  简单的类型比较好做,直接赋值就可以了。简单类型是说int,string之类的。

  有几个需要注意的地方:

  1 属性的类型是另外一个类

  2 属性是一个集合

  3 类有两个属性的类型是同一个类,例如:种子有用量及其单位,和产量及其单位,用量的单位和产量的单位是一个类型

  4 属性嵌套,就是属性的这个类型里面的属性还可能是另外一个类

  

  解决办法:

  1 用不同的attribute,普通的类型直接赋值,另外一个类的话就交给另外一个类去负责具体的映射

  2 分两次映射,然后集合赋值给商品的属性,觉得不是很好,有没有更好的办法呢?

  3 添加前缀来区分两个相同类型的不同属性

  4 嵌套,一层一层解决

  还有就是关于集合,例如:商品的包装规格集合。如何映射呢?是不是需要集合的时候,再次根据商品的ID查询包装规格集合,然后赋值给商品的包装规格属性呢?

  那就是两次访问数据库了,我想要一次访问,可以实现吗?

  结果集就是下面的格式,第一个table是商品,第二个table是包装规格,循环第一个table映射出来一个商品,但是在映射的时候如果发现属性是List类型,实际上是需要第二个结果集来配合了。如何传第二个的结果集呢?我想不出来。我的解决办法就是一个一个的映射,先映射商品,赋值给一个商品的实例。然后映射包装规格,赋值给一个List,然后把这个List赋值给商品的包装规格属性。

  不知道能否在映射商品的同时就映射包装规格集合吗?

  

  数据库的查询结果

  

 

  先定义两个attribute,一个用在属性上面,用来映射列

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.SQLite.ConApp.Common
{
    [AttributeUsage(AttributeTargets.Property)]
    
public class ColumnAttribute : Attribute
    {
        
private string _columnType;
        
/// <summary>
        
/// 数据库字段类型
        
/// </summary>
        public string ColumnType
        {
            
get { return _columnType; }
            
set { _columnType = value; }
        }
        
private string _columnName;
        
/// <summary>
        
/// 数据库字段名称
        
/// </summary>
        public string ColumnName
        {
            
get { return _columnName; }
            
set { _columnName = value; }
        }
        
private int _length;
        
/// <summary>
        
/// 数据库字段长度
        
/// </summary>
        public int Length
        {
            
get { return _length; }
            
set { _length = value; }
        }
        
private bool _isIdentity;
        
/// <summary>
        
/// 是否标识列
        
/// </summary>
        public bool IsIdentity
        {
            
get { return _isIdentity; }
            
set { _isIdentity = value; }
        }

        
public ColumnAttribute(string columnName, string columnType, int len, bool isIdentity)
        {
            
this._columnType = columnType;
            
this._columnName = columnName;
            
this._length = len;
            
this._isIdentity = isIdentity;
        }
        
public ColumnAttribute(string columnName)
            : 
this(columnName, string.Empty, 0false)
        {

        }
    }
}

  一个用在属性是复杂类型,也就是说属性的类型是另外一个类

  

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.SQLite.ConApp.Common
{
    [AttributeUsage(AttributeTargets.Property)]
    
public class ReferenceAttribute : Attribute
    {
        
private Type _referenceClassType;

        
public Type ReferenceClassType
        {
            
get { return _referenceClassType; }
            
set { _referenceClassType = value; }
        }
        
private string _preFix;

        
public string PreFix
        {
            
get { return _preFix; }
            
set { _preFix = value; }
        }

        
private ReferenceType _referenceType;

        
public ReferenceType ReferenceType
        {
            
get { return _referenceType; }
            
set { _referenceType = value; }
        }
        
public ReferenceAttribute(Type type, string preFix,ReferenceType referenceType)
        {
            
this._referenceClassType = type;
            
this._preFix = preFix;
            
this._referenceType = referenceType;
        }

    }
    
public enum ReferenceType
    {
        Ojbect,
        Collection
    }
}

 

  给这两种attribute类型定义两个映射方法

  

代码
public  class ColumnMapper
    {
       
public static void ColToProperty(ref PropertyInfo p,object entity, IDataReader reader,string preFix)
       {
           
string columnName = string.IsNullOrWhiteSpace(preFix) ? p.Name : preFix + p.Name;
          
               
if (p.PropertyType.IsEnum)
               {
                   p.SetValue(entity, Enum.Parse(p.PropertyType, reader[columnName].ToString()), 
null);
               }
               
else
               {
                   p.SetValue(entity, reader[columnName], 
null);
               }
          
       }
    }

 
public class ReferenceMapper
    {
        
public static object   ReferenceToEntity( Type type, string preFix, IDataReader  reader, Common.ReferenceType referenceType)
        {
            
string columnName = string.Empty;
           
object obj = Activator.CreateInstance(type);

            PropertyInfo[] ps 
= obj.GetType ().GetProperties();

            
for (int i = 0; i < ps.Length; i++)
            {
                columnName 
= string.IsNullOrWhiteSpace(preFix) ? ps[i].Name : preFix + ps[i].Name;
                
object[] attributes = ps[i].GetCustomAttributes(false);
                
if (attributes.Length > 0)
                {
                    
if (referenceType == Common.ReferenceType.Ojbect)
                    {
                        
if (attributes[0is Common.ColumnAttribute)
                        {
                            
if (string.IsNullOrWhiteSpace(reader[columnName].ToString()))
                                
continue;
                            ColumnMapper.ColToProperty(
ref ps[i], obj, reader, preFix);
                        }
                        
if (attributes[0is Common.ReferenceAttribute)
                        {
                            ps[i].SetValue(obj, ReferenceMapper.ReferenceToEntity((attributes[
0as Common.ReferenceAttribute).ReferenceClassType,
                               (attributes[
0as Common.ReferenceAttribute).PreFix, reader, (attributes[0as Common.ReferenceAttribute).ReferenceType), null);
                        }
                    }
                }
            }
            
return obj;
        }
    }

 

  实例实体类

  

 

代码
 [Flags]
    
public enum ProductType
    {
        Seed 
= 1,
        Fertilizer 
= 2,
        Pesticide 
= 4
    }
    
public class Seed : Common.BaseEntity
    {
        [Column(
"SeedID")]
        
public Guid SeedID
        { 
getset; }
        [Column(
"SeedName")]
        
public string SeedName
        { 
getset; }
        [Column (
"ProductType")]
        
public ProductType ProductType
        {
            
get;
            
set;
        }
        [Column(
"PlantAmount")]
        
public decimal PlantAmount
        { 
getset; }
        [Reference(
typeof(Unit), "PlantAmount", Common.ReferenceType.Ojbect )]
        
public Unit PlantAmountUnit
        { 
getset; }
        [Column(
"OutputAmount")]
        
public decimal OutputAmount
        { 
getset; }
        [Reference(
typeof(Unit), "OutputAmount", Common.ReferenceType.Ojbect )]
        
public Unit OutputAmountUnit
        { 
getset; }
        
//[Reference (typeof(SeedPkgSpec),"", Common.ReferenceType.Collection )]
        public List<SeedPkgSpec> PkgSpecs
        {
            
get;
            
set;
        }
    }

 

SeedPkgSpec
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BeautyCode.SQLite.ConApp.Common;

namespace BeautyCode.SQLite.ConApp.Entity
{
    
public class SeedPkgSpec : Common.BaseEntity 
   {
       [Column(
"PkgSpecID")]
       
public Guid PkgSpecID
       { 
getset; }
       [Column(
"SeedID")]
       
public Guid SeedID 
       { 
getset; }
       [Column(
"PkgAmount")]
       
public decimal PkgAmount
       { 
getset; }
       [Reference(
typeof(Unit ),"PkgAmount", Common.ReferenceType.Ojbect ) ]
       
public Unit PkgUnit
       { 
getset; }
       [Reference(
typeof(PkgSpec), "",  Common.ReferenceType.Ojbect )]
       
public PkgSpec PkgSpec
       {
           
get;
           
set;
       }
    }
}

 

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BeautyCode.SQLite.ConApp.Common;

namespace BeautyCode.SQLite.ConApp.Entity
{
   
public  class PkgSpec:Common.BaseEntity 
    {
       [Column(
"PkgSpecCode")]
       
public string PkgSpecCode
       { 
getset; }
       [Column(
"PkgSpecName")]
       
public string PkgSpecName
       { 
getset; }
    }
}

 

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BeautyCode.SQLite.ConApp.Common;

namespace BeautyCode.SQLite.ConApp.Entity
{
    
public class Unit : Common.BaseEntity 
    {
        [Column(
"UnitCode")]
        
public string UnitCode 
        { 
getset; }
        [Column(
"UnitCnName")]
        
public string UnitCnName
        { 
getset; }
        [Column( 
"UnitEnName")]
        
public string UnitEnName
        { 
getset; }
    }
}

  映射接口,每个实体实现下面的接口

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;


namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public interface  IEntityMapper<T> where T:Common.BaseEntity 
    {
        T RowToEntity(IDataReader  reader);

        DataRow EntityToRow(T entity);
    }
}

 

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public class SeedMapper : IEntityMapper<Entity.Seed>
    {
        
#region IEntityMapper<Seed> Members

        
public Entity.Seed RowToEntity(IDataReader reader)
        {
            Entity.Seed entity 
= new Entity.Seed();

            PropertyInfo[] ps 
= typeof(Entity.Seed).GetProperties();

            
for (int i = 0; i < ps.Length; i++)
            {
                
object[] attributes = ps[i].GetCustomAttributes(false);
                
if (attributes.Length > 0)
                {
                    
if (attributes[0is Common.ColumnAttribute)
                    {
                        ColumnMapper.ColToProperty(
ref ps[i], entity, reader, null);
                    }
                    
if (attributes[0is Common.ReferenceAttribute)
                    {
                        ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[
0as Common.ReferenceAttribute).ReferenceClassType,
                             (attributes[
0as Common.ReferenceAttribute).PreFix, reader, (attributes[0as Common.ReferenceAttribute).ReferenceType), null);

                    }
                }

            }
            
return entity;
        }



        
public System.Data.DataRow EntityToRow(Entity.Seed entity)
        {
            
throw new NotImplementedException();
        }

        
#endregion
    }
}

 

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public class SeedPkgSpecMapper:IEntityMapper<Entity.SeedPkgSpec >
    {
        
#region IEntityMapper<SeedPkgSpec> Members

        
public Entity.SeedPkgSpec RowToEntity(IDataReader  reader)
        {
            Entity.SeedPkgSpec entity 
= new Entity.SeedPkgSpec();

            PropertyInfo[] ps 
= typeof(Entity.SeedPkgSpec).GetProperties();

            
for (int i = 0; i < ps.Length; i++)
            {
                
object[] attributes = ps[i].GetCustomAttributes(false);
                
if (attributes.Length > 0)
                {
                    
if (attributes[0is Common.ColumnAttribute)
                    {
                        
if (string.IsNullOrWhiteSpace(reader[ps[i].Name].ToString()))
                            
continue;
                        ColumnMapper.ColToProperty(
ref ps[i], entity, reader, null);
                    }
                    
if (attributes[0is Common.ReferenceAttribute)
                    {
                        ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[
0as Common.ReferenceAttribute).ReferenceClassType,
                             (attributes[
0as Common.ReferenceAttribute).PreFix, reader, (attributes[0as Common.ReferenceAttribute).ReferenceType), null);

                    }
                }

            }
            
return entity;
        }

        
public System.Data.DataRow EntityToRow(Entity.SeedPkgSpec entity)
        {
            
throw new NotImplementedException();
        }

        
#endregion
    }
}

 

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public class UnitMapper:IEntityMapper<Entity.Unit>
    {
        
#region IEntityMapper<Unit> Members

        
public Entity.Unit RowToEntity(System.Data.IDataReader reader)
        {
            Entity.Unit entity 
= new Entity.Unit();

            PropertyInfo[] ps 
= typeof(Entity.Unit).GetProperties();

            
for (int i = 0; i < ps.Length; i++)
            {
                
object[] attributes = ps[i].GetCustomAttributes(false);
                
if (attributes.Length > 0)
                {
                    
if (attributes[0is Common.ColumnAttribute)
                    {
                        
if (string.IsNullOrWhiteSpace(reader[ps[i].Name].ToString()))
                            
continue;
                        ColumnMapper.ColToProperty(
ref ps[i], entity, reader, null);
                    }
                    
if (attributes[0is Common.ReferenceAttribute)
                    {
                        ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[
0as Common.ReferenceAttribute).ReferenceClassType,
                             (attributes[
0as Common.ReferenceAttribute).PreFix, reader, (attributes[0as Common.ReferenceAttribute).ReferenceType), null);

                    }
                }

            }
            
return entity;
        }

        
public System.Data.DataRow EntityToRow(Entity.Unit entity)
        {
            
throw new NotImplementedException();
        }

        
#endregion
    }
}

 

  上面的代码结构其实还是可以优化的,因为每个实体类的映射方法,里面的代码大量的重复,只是初始化的类型不一样,还有重构的余地。

  目前还差的就是映射集合,在映射商品的时候一起映射集合。

  项目可以从http://beautycode.codeplex.com/下载,或者是/Files/virusswb/BeautyCode.SQLite.ConApp.rar,希望大家踊跃参与讨论。

 

  刚才稍微重构了一下,就是实体类对应的每个映射类,其实可以抽象一个抽象类,然后实体类的映射类继承抽象类,抽象类实现接口,包含具体的实现,如果实体类的映射类需要重写的话,就override,不需要的话,默认也可以使用。

  

BaseEntityMapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public abstract class BaseEntityMapper<T> : IEntityMapper<T> where T : Common.BaseEntity
    {

        
#region IEntityMapper<T> Members

        
public virtual T RowToEntity(IDataReader reader)
        {
            T entity 
= Activator.CreateInstance<T>();

            PropertyInfo[] ps 
= typeof(T).GetProperties();

            
for (int i = 0; i < ps.Length; i++)
            {
                
object[] attributes = ps[i].GetCustomAttributes(false);
                
if (attributes.Length > 0)
                {
                    
if (attributes[0is Common.ColumnAttribute)
                    {
                        ColumnMapper.ColToProperty(
ref ps[i], entity, reader, null);
                    }
                    
if (attributes[0is Common.ReferenceAttribute)
                    {
                        ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[
0as Common.ReferenceAttribute).ReferenceClassType,
                             (attributes[
0as Common.ReferenceAttribute).PreFix, reader, (attributes[0as Common.ReferenceAttribute).ReferenceType), null);

                    }
                }

            }
            
return entity;
        }

        
public virtual DataRow EntityToRow(T entity)
        {
            
throw new NotImplementedException();
        }

        
#endregion
    }
}

 

 

  具体实体类的映射类就可以得到简化

 

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace BeautyCode.SQLite.ConApp.EntityMapper
{
    
public class SeedMapper : BaseEntityMapper<Entity.Seed >
    {
        
    }
}