我不知道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 >
{
}
}
本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/461370,如需转载请自行联系原作者