写在前面

  AutoMapper目录:

  本篇目录:

  关于AutoMapper写到这基本的东西都差不多了,上一篇定义为灵活配置篇,本篇可以定义为扩展应用篇,加一些补充,关于AutoMapper的项目应用,网上找了几篇英文文章,虽然看不懂,但是代码是相通的,感觉很不错,主要是EntityFramework中运用AutoMapper,数据访问中使用AutoMapper,有支持的,也有反对的,也有提出建议的,自己也正在摸索,希望有机会写篇文章和大家分享下。

  插一句:写这些东西,看的人真的很少,还不如像前几天大家写篇水文,来讨论下C#的好坏增加点人气,呵呵,但是如果是这种思想来编程真是不可饶恕,写这种文章的目的不一定是分享给别人,也是对自己学习的另一种修炼,毕竟肚子没有什么东西,是写不出来,也是在逼迫自己去学习,当去学习一点东西后,发现其实并不像想象的那么简单,还有很多的东西要去学习,恨只恨自己晚生了几年,还需努力。

Mapping Inheritance-映射继承

  关于映射继承,其实在“Lists and Array-集合和数组”这一节点有提到,但是只是说明下AutoMapper解决映射继承所使用的方式,这边我们说下关于AutoMapper在映射继承中的一些特性,比如下面转换示例:

1         public class Order { }
2         public class OnlineOrder : Order { }
3         public class MailOrder : Order { }
4 
5         public class OrderDto { }
6         public class OnlineOrderDto : OrderDto { }
7         public class MailOrderDto : OrderDto { }

  源对象和目标对象存在继承关系,和“Lists and Array”节点里面的的示例一样,我们首先要配置AutoMapper,添加类型映射关系和依赖关系,如下:

1             //配置 AutoMapper
2             Mapper.CreateMap<Order, OrderDto>()
3                   .Include<OnlineOrder, OnlineOrderDto>()
4                   .Include<MailOrder, MailOrderDto>();
5             Mapper.CreateMap<OnlineOrder, OnlineOrderDto>();
6             Mapper.CreateMap<MailOrder, MailOrderDto>();

  关于这三段代码的意义,在“Lists and Array”节点中也有说明,如果我们注释掉第一段代码,我们在做派生类映射转换的时候就会报错,如果我们把下面两段代码去掉,我们在做派生类映射转换的时候就会映射到基类,说明第一段代码的意义是,不仅仅包含Order到OrderDto之间的类型映射,还包含Order与OrderDto所有派生类之间的映射,但是只是声明,如果要对派生类之间进行类型映射转换,就还得需要创建派生类之间的类型映射关系。

  我们“Lists and Array”节点中这样执行类型映射转换:

1     var destinations = Mapper.Map<ParentSource[], ParentDestination[]>(sources);

  上面这段转换代码是指定了源泛型类型(Source)和目标泛型类型类型(Dest),所以AutoMapper会根据指定的类型就可以进行转换了,前提是类型映射关系配置正确,要不然就会报“AutoMapperConfigurationException”异常。如果我们不指定目标数据类型,然后就行转换会怎样呢?比如下面转换:

1     var order = new OnlineOrder();
2     var mapped = Mapper.Map(order, order.GetType(), typeof(OrderDto));
3     Console.WriteLine("mapped Type:" + mapped.GetType());

  转换效果:

【AutoMapper官方文档】DTO与Domin Model相互转换(下)

  代码中我们并没有指定目标数据类型,只是指定一个派生类的基类,如果按照我们的理解,这段代码执行的结果应该是:转换结果mapped对象的类型应该是OrderDto类型,但是确是我们希望想要的OnlineOrderDto类型,虽然我们没有指定目标类型为OnlineOrderDto,但是这一切AutoMapper都帮你做了,就是说AutoMapper会自动判断目标类型与源数据类型存在的关系,并找出最合适的派生类类型。

Queryable Extensions (LINQ)-扩展查询表达式

  注:关于Entity Framework中数据类型映射正在研究,网上找了很多英文文章,还在消化中,这一节点只是简单的说下AutoMapper查询表达式的用法,过几天再整理一篇关于实体框架中运用数据类型映射的文章,功力不够,还请包涵。

  当我们使用Entity Framework与AutoMapper结合进行查询对象转换的时候,使用Mapper.Map方法,就会发现查询结果对象中的所有属性和目标属性对象属性都会转换,当然你也可以在查询结果集中构建一个所需结果的示例,但是这样做并不是可取的,AutoMapper的作者扩展了QueryableExtensions,使得我们在查询的时候就可以实现转换,比如下面示例:

 1         public class OrderLine
 2         {
 3             public int Id { get; set; }
 4             public int OrderId { get; set; }
 5             public Item Item { get; set; }
 6             public decimal Quantity { get; set; }
 7         }
 8         public class Item
 9         {
10             public int Id { get; set; }
11             public string Name { get; set; }
12         }
13 
14         public class OrderLineDTO
15         {
16             public int Id { get; set; }
17             public int OrderId { get; set; }
18             public string Item { get; set; }
19             public decimal Quantity { get; set; }
20         }
21 
22         public List<OrderLineDTO> GetLinesForOrder(int orderId)
23         {
24             Mapper.CreateMap<OrderLine, OrderLineDTO>()
25               .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name));
26 
27             using (var context = new orderEntities())
28             {
29                 return context.OrderLines.Where(ol => ol.OrderId == orderId)
30                          .Project().To<OrderLineDTO>().ToList();
31             }
32         }

  代码中的.Project().To就是扩展的查询表达式,详细表达式代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Linq.Expressions;
  5 using System.Reflection;
  6 using System.Text.RegularExpressions;
  7 
  8 namespace DTO_AutoMapper使用详解
  9 {
 10     public static class QueryableExtensions
 11     {
 12         public static ProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source)
 13         {
 14             return new ProjectionExpression<TSource>(source);
 15         }
 16     }
 17 
 18     public class ProjectionExpression<TSource>
 19     {
 20         private static readonly Dictionary<string, Expression> ExpressionCache = new Dictionary<string, Expression>();
 21 
 22         private readonly IQueryable<TSource> _source;
 23 
 24         public ProjectionExpression(IQueryable<TSource> source)
 25         {
 26             _source = source;
 27         }
 28 
 29         public IQueryable<TDest> To<TDest>()
 30         {
 31              var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>();
 32 
 33             return _source.Select(queryExpression);
 34         }        
 35 
 36         private static Expression<Func<TSource, TDest>> GetCachedExpression<TDest>()
 37         {
 38             var key = GetCacheKey<TDest>();
 39 
 40             return ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression<Func<TSource, TDest>> : null;
 41         }
 42 
 43         private static Expression<Func<TSource, TDest>> BuildExpression<TDest>()
 44         {
 45             var sourceProperties = typeof(TSource).GetProperties();
 46             var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite);
 47             var parameterExpression = Expression.Parameter(typeof(TSource), "src");
 48             
 49             var bindings = destinationProperties
 50                                 .Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties))
 51                                 .Where(binding => binding != null);
 52 
 53             var expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression);
 54 
 55             var key = GetCacheKey<TDest>();
 56 
 57             ExpressionCache.Add(key, expression);
 58 
 59             return expression;
 60         }        
 61 
 62         private static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties)
 63         {
 64             var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name);
 65 
 66             if (sourceProperty != null)
 67             {
 68                 return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty));
 69             }
 70 
 71             var propertyNames = SplitCamelCase(destinationProperty.Name);
 72 
 73             if (propertyNames.Length == 2)
 74             {
 75                 sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]);
 76 
 77                 if (sourceProperty != null)
 78                 {
 79                     var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]);
 80 
 81                     if (sourceChildProperty != null)
 82                     {
 83                         return Expression.Bind(destinationProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty));
 84                     }
 85                 }
 86             }
 87 
 88             return null;
 89         }
 90 
 91         private static string GetCacheKey<TDest>()
 92         {
 93             return string.Concat(typeof(TSource).FullName, typeof(TDest).FullName);
 94         }
 95 
 96         private static string[] SplitCamelCase(string input)
 97         {
 98             return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim().Split(' ');
 99         }
100     }    
101 }
View Code

相关文章:

  • 2022-12-23
  • 2022-03-09
  • 2022-12-23
  • 2022-02-12
  • 2021-07-21
  • 2021-12-30
猜你喜欢
  • 2022-02-26
  • 2022-02-01
  • 2022-12-23
  • 2021-07-27
相关资源
相似解决方案