最近在用Dapper處理Sqlite。映射模型的時候不喜歡用Attribute配置,但願用相似EF的Map來配置,因此粗略的實現了一個。git
首先是主體的配置輔助類型:express
using System; using System.Collections.Concurrent; using System.Linq.Expressions; using System.Reflection; using Dapper; using DRapid.Utility.Linq.Expressions; namespace DRapid.Utility.Dapper.Map { public class InstanceMapper<T> : InstanceMapper { protected InstanceMapper() : base(typeof(T)) { } public static InstanceMapper<T> Config() { return new InstanceMapper<T>(); } public void MapColumn(string columnName, Expression<Func<T, object>> propertySelector) { var propertyName = ExpressionHelper.ReadMemberName(propertySelector); MapColumn(columnName, propertyName); } } public class InstanceMapper : SqlMapper.ITypeMap { protected InstanceMapper(Type type) { _map = new CustomPropertyTypeMap(type, GetProperty); _mapDic = new ConcurrentDictionary<string, PropertyInfo>(); _instanceType = type; } private CustomPropertyTypeMap _map; private Type _instanceType; private ConcurrentDictionary<string, PropertyInfo> _mapDic; public ConstructorInfo FindConstructor(string[] names, Type[] types) { return _map.FindConstructor(names, types); } public ConstructorInfo FindExplicitConstructor() { return _map.FindExplicitConstructor(); } public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) { return _map.GetConstructorParameter(constructor, columnName); } public SqlMapper.IMemberMap GetMember(string columnName) { return _map.GetMember(columnName); } public void MapColumn(string columnName, string propertyName) { _mapDic.AddOrUpdate(columnName, key => _instanceType.GetProperty(propertyName), (key, pro) => _instanceType.GetProperty(propertyName)); } private PropertyInfo GetProperty(Type type, string columnName) { PropertyInfo propertyInfo; var result = _mapDic.TryGetValue(columnName, out propertyInfo); return result ? propertyInfo : null; } public void Apply() { SqlMapper.SetTypeMap(_instanceType, this); } public static InstanceMapper Config(Type type) { return new InstanceMapper(type); } } }
這裏是其中引用的一個輔助方法的實現api
public static string ReadMemberName<T>(Expression<Func<T, object>> expression) { var body = expression.Body; /*這裏須要考慮轉型表達式*/ if (body.NodeType == ExpressionType.Convert) body = ((UnaryExpression) body).Operand; Trace.Assert(body.NodeType == ExpressionType.MemberAccess, "表達式必須是成員訪問或者是帶轉型的成員訪問"); var accessMember = (MemberExpression) body; return accessMember.Member.Name; }
而後是一些鏈式調用的擴展支持app
using System; using System.Linq.Expressions; namespace DRapid.Utility.Dapper.Map { public static class InstanceMapperExtension { public static InstanceMapper<T> Use<T>(this InstanceMapper<T> mapper, string columnName, string propertyName) { mapper.MapColumn(columnName, propertyName); return mapper; } public static InstanceMapper<T> Use<T>(this InstanceMapper<T> mapper, string columnName, Expression<Func<T, object>> propertySelector) { mapper.MapColumn(columnName, propertySelector); return mapper; } } }
因爲有靜態訪問入口,因此配置通常分佈在各個類的靜態構造函數中,從而防止重複配置。
因此,對於一個dto類型:函數
public class PointInfo { public byte[] Detail { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } public int Count { get; set; } }
能夠使用如下配置代碼進行配置:this
InstanceMapper<PointInfo>.Config() .Use("STATLG", s => s.Longitude) .Use("STATLA", s => s.Latitude) .Use("FREQUERYCOUNT", s => s.Count) .Use("FREQDB", s => s.Detail) .Apply();
結束。spa