., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ., ..
通过上面的SQL语句,会发现,虽然数据访问层代码写的简单了,但是查询的字段并不是我们想要的,也就是说查询发生在映射之前,可以想象如果存在上百万的数据或是上百行,使用AutoMapper进行映射转换是多么的不靠谱,难道EntityFramework和AutoMapper就没有缘分?或者只是EntityFramework的一厢情愿?请看下面。
女人的伟大?做了以下工作:
QueryableExtensions 2 { ProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source) 4 { ProjectionExpression<TSource>(source); 6 } 7 } ProjectionExpression<TSource> 10 { Dictionary<string, Expression> ExpressionCache = new Dictionary<string, Expression>(); IQueryable<TSource> _source; ProjectionExpression(IQueryable<TSource> source) 16 { 17 _source = source; 18 } IQueryable<TDest> To<TDest>() 21 { 22 var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>(); _source.Select(queryExpression); 25 } Expression<Func<TSource, TDest>> GetCachedExpression<TDest>() 28 { 29 var key = GetCacheKey<TDest>(); ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression<Func<TSource, TDest>> : null; 32 } Expression<Func<TSource, TDest>> BuildExpression<TDest>() 35 { 36 var sourceProperties = typeof(TSource).GetProperties(); 37 var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite); ); bindings = destinationProperties 41 .Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties)) 42 .Where(binding => binding != null); expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression); key = GetCacheKey<TDest>(); 47 48 ExpressionCache.Add(key, expression); expression; 51 } MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties) 54 { 55 var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name); (sourceProperty != null) 58 { 59 return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty)); 60 } propertyNames = SplitCamelCase(destinationProperty.Name); (propertyNames.Length == 2) 65 { 66 sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]); (sourceProperty != null) 69 { 70 var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]); (sourceChildProperty != null) 73 { 74 return Expression.Bind(destinationProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty)); 75 } 76 } 77 } ; 80 } GetCacheKey<TDest>() 83 { .Concat(typeof(TSource).FullName, typeof(TDest).FullName); 85 } [] SplitCamelCase(string input) 88 { , , RegexOptions.Compiled).Trim().Split(' '); 90 } 91 }
修改示例代码:
1 Mapper.CreateMap<Order, OrderConsignee>(); 2 var details = context.Orders.Project().To<OrderConsignee>();
通过AutoMapper所做的努力,使得代码更加简化,只要配置一个类型映射,传递目标类型,就可以得到我们想要的转换对象,代码如此简洁,我们再来看下生成SQL代码: