自定义ORM系列(一)利用attribute实现简单的reader=>entity和reader=>List<entity>映射
2010-07-09 15:27 Virus-BeautyCode 阅读(3816) 评论(7) 编辑 收藏 举报我不知道NH的ORM具体如何实现的,我的想法就是通过字段名称和属性名称的对应关系来实现赋值。
简单的类型比较好做,直接赋值就可以了。简单类型是说int,string之类的。
有几个需要注意的地方:
1 属性的类型是另外一个类
2 属性是一个集合
3 类有两个属性的类型是同一个类,例如:种子有用量及其单位,和产量及其单位,用量的单位和产量的单位是一个类型
4 属性嵌套,就是属性的这个类型里面的属性还可能是另外一个类
解决办法:
1 用不同的attribute,普通的类型直接赋值,另外一个类的话就交给另外一个类去负责具体的映射
2 分两次映射,然后集合赋值给商品的属性,觉得不是很好,有没有更好的办法呢?
3 添加前缀来区分两个相同类型的不同属性
4 嵌套,一层一层解决
还有就是关于集合,例如:商品的包装规格集合。如何映射呢?是不是需要集合的时候,再次根据商品的ID查询包装规格集合,然后赋值给商品的包装规格属性呢?
那就是两次访问数据库了,我想要一次访问,可以实现吗?
结果集就是下面的格式,第一个table是商品,第二个table是包装规格,循环第一个table映射出来一个商品,但是在映射的时候如果发现属性是List类型,实际上是需要第二个结果集来配合了。如何传第二个的结果集呢?我想不出来。我的解决办法就是一个一个的映射,先映射商品,赋值给一个商品的实例。然后映射包装规格,赋值给一个List,然后把这个List赋值给商品的包装规格属性。
不知道能否在映射商品的同时就映射包装规格集合吗?
数据库的查询结果
先定义两个attribute,一个用在属性上面,用来映射列
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, 0, false)
{
}
}
}
一个用在属性是复杂类型,也就是说属性的类型是另外一个类
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 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[0] is Common.ColumnAttribute)
{
if (string.IsNullOrWhiteSpace(reader[columnName].ToString()))
continue;
ColumnMapper.ColToProperty(ref ps[i], obj, reader, preFix);
}
if (attributes[0] is Common.ReferenceAttribute)
{
ps[i].SetValue(obj, ReferenceMapper.ReferenceToEntity((attributes[0] as Common.ReferenceAttribute).ReferenceClassType,
(attributes[0] as Common.ReferenceAttribute).PreFix, reader, (attributes[0] as Common.ReferenceAttribute).ReferenceType), null);
}
}
}
}
return obj;
}
}
实例实体类
public enum ProductType
{
Seed = 1,
Fertilizer = 2,
Pesticide = 4
}
public class Seed : Common.BaseEntity
{
[Column("SeedID")]
public Guid SeedID
{ get; set; }
[Column("SeedName")]
public string SeedName
{ get; set; }
[Column ("ProductType")]
public ProductType ProductType
{
get;
set;
}
[Column("PlantAmount")]
public decimal PlantAmount
{ get; set; }
[Reference(typeof(Unit), "PlantAmount", Common.ReferenceType.Ojbect )]
public Unit PlantAmountUnit
{ get; set; }
[Column("OutputAmount")]
public decimal OutputAmount
{ get; set; }
[Reference(typeof(Unit), "OutputAmount", Common.ReferenceType.Ojbect )]
public Unit OutputAmountUnit
{ get; set; }
//[Reference (typeof(SeedPkgSpec),"", Common.ReferenceType.Collection )]
public List<SeedPkgSpec> PkgSpecs
{
get;
set;
}
}
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
{ get; set; }
[Column("SeedID")]
public Guid SeedID
{ get; set; }
[Column("PkgAmount")]
public decimal PkgAmount
{ get; set; }
[Reference(typeof(Unit ),"PkgAmount", Common.ReferenceType.Ojbect ) ]
public Unit PkgUnit
{ get; set; }
[Reference(typeof(PkgSpec), "", Common.ReferenceType.Ojbect )]
public PkgSpec PkgSpec
{
get;
set;
}
}
}
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
{ get; set; }
[Column("PkgSpecName")]
public string PkgSpecName
{ get; set; }
}
}
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
{ get; set; }
[Column("UnitCnName")]
public string UnitCnName
{ get; set; }
[Column( "UnitEnName")]
public string UnitEnName
{ get; set; }
}
}
映射接口,每个实体实现下面的接口
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.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[0] is Common.ColumnAttribute)
{
ColumnMapper.ColToProperty(ref ps[i], entity, reader, null);
}
if (attributes[0] is Common.ReferenceAttribute)
{
ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[0] as Common.ReferenceAttribute).ReferenceClassType,
(attributes[0] as Common.ReferenceAttribute).PreFix, reader, (attributes[0] as Common.ReferenceAttribute).ReferenceType), null);
}
}
}
return entity;
}
public System.Data.DataRow EntityToRow(Entity.Seed entity)
{
throw new NotImplementedException();
}
#endregion
}
}
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[0] is Common.ColumnAttribute)
{
if (string.IsNullOrWhiteSpace(reader[ps[i].Name].ToString()))
continue;
ColumnMapper.ColToProperty(ref ps[i], entity, reader, null);
}
if (attributes[0] is Common.ReferenceAttribute)
{
ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[0] as Common.ReferenceAttribute).ReferenceClassType,
(attributes[0] as Common.ReferenceAttribute).PreFix, reader, (attributes[0] as Common.ReferenceAttribute).ReferenceType), null);
}
}
}
return entity;
}
public System.Data.DataRow EntityToRow(Entity.SeedPkgSpec entity)
{
throw new NotImplementedException();
}
#endregion
}
}
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[0] is Common.ColumnAttribute)
{
if (string.IsNullOrWhiteSpace(reader[ps[i].Name].ToString()))
continue;
ColumnMapper.ColToProperty(ref ps[i], entity, reader, null);
}
if (attributes[0] is Common.ReferenceAttribute)
{
ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[0] as Common.ReferenceAttribute).ReferenceClassType,
(attributes[0] as Common.ReferenceAttribute).PreFix, reader, (attributes[0] as 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,不需要的话,默认也可以使用。
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[0] is Common.ColumnAttribute)
{
ColumnMapper.ColToProperty(ref ps[i], entity, reader, null);
}
if (attributes[0] is Common.ReferenceAttribute)
{
ps[i].SetValue(entity, ReferenceMapper.ReferenceToEntity((attributes[0] as Common.ReferenceAttribute).ReferenceClassType,
(attributes[0] as Common.ReferenceAttribute).PreFix, reader, (attributes[0] as Common.ReferenceAttribute).ReferenceType), null);
}
}
}
return entity;
}
public virtual DataRow EntityToRow(T entity)
{
throw new NotImplementedException();
}
#endregion
}
}
具体实体类的映射类就可以得到简化
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 >
{
}
}