找回密码
 立即注册
首页 业界区 业界 婶可忍叔不可忍的AutoMapper,你还用吗?

婶可忍叔不可忍的AutoMapper,你还用吗?

肿圬后 6 天前
AutoMapper是让人又爱又恨的项目



  • 爱它是因为它解决了一些问题,很多项目都有用,下载量很大,受众很广。
  • 恨它是因为它诸多反人类的设计。
  • 为此本人开源项目PocoEmit对标AutoMapper。
1. AutoMapper反人类设计

1.1 AutoMapper注册代码
  1. services.AddAutoMapper(cfg => cfg.CreateMap<User, UserDTO>());
复制代码
User和UserDTO除了类名不一样,其他都一样,怎么看这行代码都多余。
需要转化的类型越多,多余的代码就越多。
类型转化不应该就是个静态方法吗?而且AutoMapper注册却依赖微软容器,
本人觉得AutoMapper设计的太反人类了。
1.2 PocoEmit对于大部分转化是不需要手动配置



  • PocoEmit可以轻松的定义静态实例
  • PocoEmit静态实例可以用来定义静态委托字段,当静态方法使用
  1. UserDTO dto = PocoEmit.Mapper.Default.Convert<User, UserDTO>(new User());
复制代码
  1. public static readonly Func<User, UserDTO> UserDTOConvert = PocoEmit.Mapper.Default.GetConvertFunc<User, UserDTO>();
复制代码
2. AutoMapper的性能差强人意

2.1 以下是AutoMapper官网例子与PocoEmit.Mapper的对比



  • Customer转化为CustomerDTO(嵌套多个子对象、数组及列表)
  • Auto是执行AutoMapper的IMapper.Map方法
  • Poco是执行PocoEmit.Mapper的IMapper.Convert方法
  • PocoFunc是执行PocoEmit.Mapper生成的委托
MethodMeanErrorStdDevMedianRatioRatioSDGen0AllocatedAlloc RatioAuto89.30 ns1.006 ns1.118 ns90.17 ns1.460.030.0260448 B1.08Poco61.31 ns1.036 ns1.194 ns61.25 ns1.000.030.0241416 B1.00PocoFunc42.56 ns0.066 ns0.073 ns42.56 ns0.690.010.0223384 B0.92


  • Auto耗时比Poco多50%左右
  • Auto耗时是PocoFunc的两倍多
2.2 能不能用AutoMapper生成委托来提高性能呢



  • 既可以说能也可以说不能
  • 说能是因为AutoMapper确实提供了该功能
  • 说不能是因为AutoMapper没打算给用户用
2.2.1 AutoMapper生成委托有点麻烦
  1. var configuration = _auto.ConfigurationProvider.Internal();
  2. var mapRequest = new MapRequest(new TypePair(typeof(Customer), typeof(CustomerDTO)));
  3. Func<Customer, CustomerDTO, ResolutionContext, CustomerDTO> autoFunc = configuration.GetExecutionPlan<Customer, CustomerDTO>(mapRequest);
复制代码
作为对比PocoEmit.Mapper就简单的多了
  1. Func<Customer, CustomerDTO> pocoFunc = PocoEmit.Mapper.Default.GetConvertFunc<Customer, CustomerDTO>();
复制代码
2.2.2 调用AutoMapper生成的委托更麻烦



  • 参数ResolutionContext没有公开的构造函数,也找不到公开的实例
  • 只能通过反射获得ResolutionContext的实例
  1. var field = typeof(AutoMapper.Mapper).GetField("_defaultContext", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  2. ResolutionContext resolutionContext = field.GetValue(_auto) as ResolutionContext;
复制代码
2.2.3 加入AutoMapper生成委托再对比一下

MethodMeanErrorStdDevMedianRatioRatioSDGen0AllocatedAlloc RatioAuto89.30 ns1.006 ns1.118 ns90.17 ns1.460.030.0260448 B1.08AutoFunc56.04 ns0.103 ns0.119 ns56.03 ns0.910.020.0260448 B1.08Poco61.31 ns1.036 ns1.194 ns61.25 ns1.000.030.0241416 B1.00PocoFunc42.56 ns0.066 ns0.073 ns42.56 ns0.690.010.0223384 B0.92


  • AutoMapper生成委托确实也快了不少
  • 从百分比来看即使不生成委托,AutoMapper也慢不了多少?没有数量级的区别,能忍? --- 反问句
2.3 简单类型转化对比



  • User转UserDTO,只有两个简单属性
MethodMeanErrorStdDevRatioGen0AllocatedAlloc RatioAuto35.436 ns0.0455 ns0.0505 ns1.570.001932 B0.50AutoFunc4.159 ns0.0847 ns0.0906 ns0.180.001932 B0.50Poco22.607 ns0.1754 ns0.1801 ns1.000.003764 B1.00PocoFunc3.818 ns0.0176 ns0.0180 ns0.170.001932 B0.50


  • Auto耗时是AutoFunc差不多十倍,差出一个数量级了(回答了前面的反问)
  • AutoFunc耗时比PocoFunc稍多,这说明AutoMapper复杂类型转化性能非常不好,简单类型转化可能还能凑合
  • 关键是性能好生成的委托AutoMapper不给用啊,“婶可忍叔不可忍”啊!
3. AutoMapper生成的代码能通过代码审核吗?

3.1 还是AutoMapper官网那个例子生成以下代码
  1. T __f<T>(System.Func<T> f) => f();
  2. CustomerDTO _autoMap(Customer source, CustomerDTO destination, ResolutionContext context)
  3. {
  4.     return (source == null) ?
  5.         (destination == null) ? (CustomerDTO)null : destination :
  6.         __f(() => {
  7.             CustomerDTO typeMapDestination = null;
  8.             typeMapDestination = destination ?? new CustomerDTO();
  9.             try
  10.             {
  11.                 typeMapDestination.Id = source.Id;
  12.             }
  13.             catch (Exception ex)
  14.             {
  15.                 throw TypeMapPlanBuilder.MemberMappingError(
  16.                     ex,
  17.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  18.             }
  19.             try
  20.             {
  21.                 typeMapDestination.Name = source.Name;
  22.             }
  23.             catch (Exception ex)
  24.             {
  25.                 throw TypeMapPlanBuilder.MemberMappingError(
  26.                     ex,
  27.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  28.             }
  29.             try
  30.             {
  31.                 Address resolvedValue = null;
  32.                 Address mappedValue = null;
  33.                 resolvedValue = source.Address;
  34.                 mappedValue = (resolvedValue == null) ? (Address)null :
  35.                     ((Func)((
  36.                         Address source_1,
  37.                         Address destination_1,
  38.                         ResolutionContext context) => //Address
  39.                         (source_1 == null) ?
  40.                             (destination_1 == null) ? (Address)null : destination_1 :
  41.                             __f(() => {
  42.                                 Address typeMapDestination_1 = null;
  43.                                 typeMapDestination_1 = destination_1 ?? new Address();
  44.                                 try
  45.                                 {
  46.                                     typeMapDestination_1.Id = source_1.Id;
  47.                                 }
  48.                                 catch (Exception ex)
  49.                                 {
  50.                                     throw TypeMapPlanBuilder.MemberMappingError(
  51.                                         ex,
  52.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  53.                                 }
  54.                                 try
  55.                                 {
  56.                                     typeMapDestination_1.Street = source_1.Street;
  57.                                 }
  58.                                 catch (Exception ex)
  59.                                 {
  60.                                     throw TypeMapPlanBuilder.MemberMappingError(
  61.                                         ex,
  62.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  63.                                 }
  64.                                 try
  65.                                 {
  66.                                     typeMapDestination_1.City = source_1.City;
  67.                                 }
  68.                                 catch (Exception ex)
  69.                                 {
  70.                                     throw TypeMapPlanBuilder.MemberMappingError(
  71.                                         ex,
  72.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  73.                                 }
  74.                                 try
  75.                                 {
  76.                                     typeMapDestination_1.Country = source_1.Country;
  77.                                 }
  78.                                 catch (Exception ex)
  79.                                 {
  80.                                     throw TypeMapPlanBuilder.MemberMappingError(
  81.                                         ex,
  82.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  83.                                 }
  84.                                 return typeMapDestination_1;
  85.                             })))
  86.                     .Invoke(
  87.                         resolvedValue,
  88.                         (destination == null) ? (Address)null :
  89.                             typeMapDestination.Address,
  90.                         context);
  91.                 typeMapDestination.Address = mappedValue;
  92.             }
  93.             catch (Exception ex)
  94.             {
  95.                 throw TypeMapPlanBuilder.MemberMappingError(
  96.                     ex,
  97.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  98.             }
  99.             try
  100.             {
  101.                 Address resolvedValue_1 = null;
  102.                 AddressDTO mappedValue_1 = null;
  103.                 resolvedValue_1 = source.HomeAddress;
  104.                 mappedValue_1 = (resolvedValue_1 == null) ? (AddressDTO)null :
  105.                     ((Func)((
  106.                         Address source_2,
  107.                         AddressDTO destination_2,
  108.                         ResolutionContext context) => //AddressDTO
  109.                         (source_2 == null) ?
  110.                             (destination_2 == null) ? (AddressDTO)null : destination_2 :
  111.                             __f(() => {
  112.                                 AddressDTO typeMapDestination_2 = null;
  113.                                 typeMapDestination_2 = destination_2 ?? new AddressDTO();
  114.                                 try
  115.                                 {
  116.                                     typeMapDestination_2.Id = source_2.Id;
  117.                                 }
  118.                                 catch (Exception ex)
  119.                                 {
  120.                                     throw TypeMapPlanBuilder.MemberMappingError(
  121.                                         ex,
  122.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  123.                                 }
  124.                                 try
  125.                                 {
  126.                                     typeMapDestination_2.City = source_2.City;
  127.                                 }
  128.                                 catch (Exception ex)
  129.                                 {
  130.                                     throw TypeMapPlanBuilder.MemberMappingError(
  131.                                         ex,
  132.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  133.                                 }
  134.                                 try
  135.                                 {
  136.                                     typeMapDestination_2.Country = source_2.Country;
  137.                                 }
  138.                                 catch (Exception ex)
  139.                                 {
  140.                                     throw TypeMapPlanBuilder.MemberMappingError(
  141.                                         ex,
  142.                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  143.                                 }
  144.                                 return typeMapDestination_2;
  145.                             })))
  146.                     .Invoke(
  147.                         resolvedValue_1,
  148.                         (destination == null) ? (AddressDTO)null :
  149.                             typeMapDestination.HomeAddress,
  150.                         context);
  151.                 typeMapDestination.HomeAddress = mappedValue_1;
  152.             }
  153.             catch (Exception ex)
  154.             {
  155.                 throw TypeMapPlanBuilder.MemberMappingError(
  156.                     ex,
  157.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  158.             }
  159.             try
  160.             {
  161.                 Address[] resolvedValue_2 = null;
  162.                 AddressDTO[] mappedValue_2 = null;
  163.                 resolvedValue_2 = source.Addresses;
  164.                 mappedValue_2 = (resolvedValue_2 == null) ?
  165.                     Array.Empty() :
  166.                     __f(() => {
  167.                         AddressDTO[] destinationArray = null;
  168.                         int destinationArrayIndex = default;
  169.                         destinationArray = new AddressDTO[resolvedValue_2.Length];
  170.                         destinationArrayIndex = default(int);
  171.                         int sourceArrayIndex = default;
  172.                         Address sourceItem = null;
  173.                         sourceArrayIndex = default(int);
  174.                         while (true)
  175.                         {
  176.                             if ((sourceArrayIndex < resolvedValue_2.Length))
  177.                             {
  178.                                 sourceItem = resolvedValue_2[sourceArrayIndex];
  179.                                 destinationArray[destinationArrayIndex++] = ((Func)((
  180.                                     Address source_2,
  181.                                     AddressDTO destination_2,
  182.                                     ResolutionContext context) => //AddressDTO
  183.                                     (source_2 == null) ?
  184.                                         (destination_2 == null) ? (AddressDTO)null : destination_2 :
  185.                                         __f(() => {
  186.                                             AddressDTO typeMapDestination_2 = null;
  187.                                             typeMapDestination_2 = destination_2 ?? new AddressDTO();
  188.                                             try
  189.                                             {
  190.                                                 typeMapDestination_2.Id = source_2.Id;
  191.                                             }
  192.                                             catch (Exception ex)
  193.                                             {
  194.                                                 throw TypeMapPlanBuilder.MemberMappingError(
  195.                                                     ex,
  196.                                                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  197.                                             }
  198.                                             try
  199.                                             {
  200.                                                 typeMapDestination_2.City = source_2.City;
  201.                                             }
  202.                                             catch (Exception ex)
  203.                                             {
  204.                                                 throw TypeMapPlanBuilder.MemberMappingError(
  205.                                                     ex,
  206.                                                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  207.                                             }
  208.                                             try
  209.                                             {
  210.                                                 typeMapDestination_2.Country = source_2.Country;
  211.                                             }
  212.                                             catch (Exception ex)
  213.                                             {
  214.                                                 throw TypeMapPlanBuilder.MemberMappingError(
  215.                                                     ex,
  216.                                                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  217.                                             }
  218.                                             return typeMapDestination_2;
  219.                                         })))
  220.                                 .Invoke(
  221.                                     sourceItem,
  222.                                     (AddressDTO)null,
  223.                                     context);
  224.                                 sourceArrayIndex++;
  225.                             }
  226.                             else
  227.                             {
  228.                                 goto LoopBreak;
  229.                             }
  230.                         }
  231.                     LoopBreak:;
  232.                         return destinationArray;
  233.                     });
  234.                 typeMapDestination.Addresses = mappedValue_2;
  235.             }
  236.             catch (Exception ex)
  237.             {
  238.                 throw TypeMapPlanBuilder.MemberMappingError(
  239.                     ex,
  240.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  241.             }
  242.             try
  243.             {
  244.                 List resolvedValue_3 = null;
  245.                 List mappedValue_3 = null;
  246.                 resolvedValue_3 = source.WorkAddresses;
  247.                 mappedValue_3 = (resolvedValue_3 == null) ?
  248.                     new List() :
  249.                     __f(() => {
  250.                         List collectionDestination = null;
  251.                         List passedDestination = null;
  252.                         passedDestination = (destination == null) ? (List)null :
  253.                             typeMapDestination.WorkAddresses;
  254.                         collectionDestination = passedDestination ?? new List();
  255.                         collectionDestination.Clear();
  256.                         List.Enumerator enumerator = default;
  257.                         Address item = null;
  258.                         enumerator = resolvedValue_3.GetEnumerator();
  259.                         try
  260.                         {
  261.                             while (true)
  262.                             {
  263.                                 if (enumerator.MoveNext())
  264.                                 {
  265.                                     item = enumerator.Current;
  266.                                     collectionDestination.Add(((Func)((
  267.                                         Address source_2,
  268.                                         AddressDTO destination_2,
  269.                                         ResolutionContext context) => //AddressDTO
  270.                                         (source_2 == null) ?
  271.                                             (destination_2 == null) ? (AddressDTO)null : destination_2 :
  272.                                             __f(() => {
  273.                                                 AddressDTO typeMapDestination_2 = null;
  274.                                                 typeMapDestination_2 = destination_2 ?? new AddressDTO();
  275.                                                 try
  276.                                                 {
  277.                                                     typeMapDestination_2.Id = source_2.Id;
  278.                                                 }
  279.                                                 catch (Exception ex)
  280.                                                 {
  281.                                                     throw TypeMapPlanBuilder.MemberMappingError(
  282.                                                         ex,
  283.                                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  284.                                                 }
  285.                                                 try
  286.                                                 {
  287.                                                     typeMapDestination_2.City = source_2.City;
  288.                                                 }
  289.                                                 catch (Exception ex)
  290.                                                 {
  291.                                                     throw TypeMapPlanBuilder.MemberMappingError(
  292.                                                         ex,
  293.                                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  294.                                                 }
  295.                                                 try
  296.                                                 {
  297.                                                     typeMapDestination_2.Country = source_2.Country;
  298.                                                 }
  299.                                                 catch (Exception ex)
  300.                                                 {
  301.                                                     throw TypeMapPlanBuilder.MemberMappingError(
  302.                                                         ex,
  303.                                                         default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  304.                                                 }
  305.                                                 return typeMapDestination_2;
  306.                                             })))
  307.                                     .Invoke(
  308.                                         item,
  309.                                         (AddressDTO)null,
  310.                                         context));
  311.                                 }
  312.                                 else
  313.                                 {
  314.                                     goto LoopBreak_1;
  315.                                 }
  316.                             }
  317.                         LoopBreak_1:;
  318.                         }
  319.                         finally
  320.                         {
  321.                             enumerator.Dispose();
  322.                         }
  323.                         return collectionDestination;
  324.                     });
  325.                 typeMapDestination.WorkAddresses = mappedValue_3;
  326.             }
  327.             catch (Exception ex)
  328.             {
  329.                 throw TypeMapPlanBuilder.MemberMappingError(
  330.                     ex,
  331.                     default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
  332.             }
  333.             return typeMapDestination;
  334.         });
  335. }   
复制代码
3.2 以下是PocoEmit.Mapper生成的代码
  1. T __f<T>(System.Func<T> f) => f();
  2. CustomerDTO _pocoConvert(Customer source)
  3. {
  4.     CustomerDTO dest = null;
  5.     return (source == (Customer)null) ? (CustomerDTO)null :
  6.         __f(() => {
  7.             dest = new CustomerDTO();
  8.             int member0 = default;
  9.             string member1 = null;
  10.             Address member2 = null;
  11.             Address member3 = null;
  12.             Address[] member4 = null;
  13.             List member5 = null;
  14.             member0 = source.Id;
  15.             dest.Id = member0;
  16.             member1 = source.Name;
  17.             dest.Name = member1;
  18.             member2 = source.Address;
  19.             if ((member2 != null))
  20.             {
  21.                 dest.Address = member2;
  22.             }
  23.             member3 = source.HomeAddress;
  24.             if ((member3 != null))
  25.             {
  26.                 // { The block result will be assigned to `dest.HomeAddress`
  27.                 AddressDTO dest_1 = null;
  28.                 dest.HomeAddress = (member3 == (Address)null) ? (AddressDTO)null :
  29.                     __f(() => {
  30.                         dest_1 = new AddressDTO();
  31.                         int member0_1 = default;
  32.                         string member1_1 = null;
  33.                         string member2_1 = null;
  34.                         member0_1 = member3.Id;
  35.                         dest_1.Id = member0_1;
  36.                         member1_1 = member3.City;
  37.                         dest_1.City = member1_1;
  38.                         member2_1 = member3.Country;
  39.                         dest_1.Country = member2_1;
  40.                         return dest_1;
  41.                     });
  42.                 // } end of block assignment;
  43.             }
  44.             member4 = source.Addresses;
  45.             if ((member4 != null))
  46.             {
  47.                 // { The block result will be assigned to `dest.Addresses`
  48.                 int count = default;
  49.                 AddressDTO[] dest_2 = null;
  50.                 int index = default;
  51.                 Address sourceItem = null;
  52.                 count = member4.Length;
  53.                 dest_2 = new AddressDTO[count];
  54.                 index = 0;
  55.                 while (true)
  56.                 {
  57.                     if ((index < count))
  58.                     {
  59.                         sourceItem = member4[index];
  60.                         // { The block result will be assigned to `dest_2[index]`
  61.                         AddressDTO dest_3 = null;
  62.                         dest_2[index] = (sourceItem == (Address)null) ? (AddressDTO)null :
  63.                             __f(() => {
  64.                                 dest_3 = new AddressDTO();
  65.                                 int member0_2 = default;
  66.                                 string member1_2 = null;
  67.                                 string member2_2 = null;
  68.                                 member0_2 = sourceItem.Id;
  69.                                 dest_3.Id = member0_2;
  70.                                 member1_2 = sourceItem.City;
  71.                                 dest_3.City = member1_2;
  72.                                 member2_2 = sourceItem.Country;
  73.                                 dest_3.Country = member2_2;
  74.                                 return dest_3;
  75.                             });
  76.                         // } end of block assignment
  77.                         index++;
  78.                     }
  79.                     else
  80.                     {
  81.                         goto forLabel;
  82.                     }
  83.                 }
  84.             forLabel:;
  85.                 dest.Addresses = dest_2;
  86.                 // } end of block assignment;
  87.             }
  88.             member5 = source.WorkAddresses;
  89.             if ((member5 != null))
  90.             {
  91.                 // { The block result will be assigned to `dest.WorkAddresses`
  92.                 List dest_4 = null;
  93.                 dest_4 = new List(member5.Count);
  94.                 int index_1 = default;
  95.                 int len = default;
  96.                 index_1 = 0;
  97.                 len = member5.Count;
  98.                 while (true)
  99.                 {
  100.                     if ((index_1 < len))
  101.                     {
  102.                         Address sourceItem_1 = null;
  103.                         AddressDTO destItem = null;
  104.                         sourceItem_1 = member5[index_1];
  105.                         // { The block result will be assigned to `destItem`
  106.                         AddressDTO dest_5 = null;
  107.                         destItem = (sourceItem_1 == (Address)null) ? (AddressDTO)null :
  108.                         __f(() => {
  109.                                     dest_5 = new AddressDTO();
  110.                                     int member0_3 = default;
  111.                                     string member1_3 = null;
  112.                                     string member2_3 = null;
  113.                                     member0_3 = sourceItem_1.Id;
  114.                                     dest_5.Id = member0_3;
  115.                                     member1_3 = sourceItem_1.City;
  116.                                     dest_5.City = member1_3;
  117.                                     member2_3 = sourceItem_1.Country;
  118.                                     dest_5.Country = member2_3;
  119.                                     return dest_5;
  120.                                 });
  121.                         // } end of block assignment;
  122.                         dest_4.Add(destItem);
  123.                         index_1++;
  124.                     }
  125.                     else
  126.                     {
  127.                         goto forLabel_1;
  128.                     }
  129.                 }
  130.             forLabel_1:;
  131.                 dest.WorkAddresses = dest_4;
  132.                 // } end of block assignment;
  133.             }
  134.             CustomerConvertBench.ConvertAddressCity(
  135.                 source,
  136.                 dest);
  137.             return dest;
  138.         });
  139. }
复制代码
3.3 简单对比如下



  • AutoMapper生成代码三百多行,PocoEmit.Mapper一百多行,AutoMapper代码量是两倍以上
  • AutoMapper生成大量try catch,哪怕是int对int赋值也要try
  • AutoMapper用迭代器Enumerator访问列表,PocoEmit.Mapper用索引器
  • AutoMapper这些区别应该是导致性能差的部分原因
3.4 如何获取AutoMapper生成的代码
  1. LambdaExpression expression = _auto.ConfigurationProvider.BuildExecutionPlan(typeof(Customer), typeof(CustomerDTO));   
复制代码
3.4.1 如果要查看更可读的代码推荐使用FastExpressionCompiler



  • 可以使用nuget安装
  • 前面的例子就是使用FastExpressionCompiler再手动整理了一下
  1. string code = FastExpressionCompiler.ToCSharpPrinter.ToCSharpString(expression);
复制代码
3.4.2 PocoEmit获取生成代码更简单
  1. Expression<Func<Customer, CustomerDTO>> expression =  PocoEmit.Mapper.Default.BuildConverter<Customer, CustomerDTO>();
  2. string code = FastExpressionCompiler.ToCSharpPrinter.ToCSharpString(expression);
复制代码
3.4.3 PocoEmit生成代码扩展性



  • PocoEmit可以获取委托表达式自己来编译委托
  • PocoEmit通过PocoEmit.Builders.Compiler.Instance来编译,可以对Instance进行覆盖来扩展
  • 通过实现Compiler类,只需要重写CompileFunc和CompileAction两个方法
  • 可以使用FastExpressionCompiler来实现Compiler类
4. AutoMapper枚举逻辑问题
  1. public enum MyColor
  2. {
  3.     None = 0,
  4.     Red = 1,
  5.     Green = 2,
  6.     Blue = 3,
  7. }
  8. ConsoleColor color = ConsoleColor.DarkBlue;
  9. // Red
  10. MyColor autoColor = _auto.Map<ConsoleColor, MyColor>(color);
  11. // None
  12. MyColor pocoColor = PocoEmit.Mapper.Default.Convert<ConsoleColor, MyColor>(color);
复制代码


  • AutoMapper先按枚举名转化,失败再按值转化,不支持的DarkBlue被AutoMapper转化为Red
  • 不同类型的枚举值转化没有意义,定义枚举可以不指定值
  • AutoMapper这完全是犯了画蛇添足的错误
  • AutoMapper还有哪些槽点欢迎大家在评论区指出
5. PocoEmit可扩展架构

5.1 nuget安装PocoEmit可获得基础功能



  • 通过PocoEmit可以读写实体的属性
  • PocoEmit可以通过PocoEmit.Poco转化基础类型和枚举
  • PocoEmit.Poco支持注册转化表达式
5.2 nuget安装PocoEmit.Mapper获得更多功能



  • PocoEmit.Mapper可以支持PocoEmit.Poco的所有功能
  • PocoEmit.Mapper可以支持自定义实体类型(不支持集合(含数组、列表及字典)成员)的转化和复制
5.3 nuget安装PocoEmit.Collections扩展集合功能



  • 通过UseCollection扩展方法给PocoEmit.Mapper增加集合功能
  • 扩展后PocoEmit.Mapper支持集合(含数组、列表及字典)的转化和复制
  • 支持实体类型包含集合成员的转化和复制
  • 嫌麻烦的同学可以直接安装PocoEmit.Collections并配置UseCollection
源码托管地址: https://github.com/donetsoftwork/MyEmit ,也欢迎大家直接查看源码。
gitee同步更新:https://gitee.com/donetsoftwork/MyEmit
如果大家喜欢请动动您发财的小手手帮忙点一下Star。

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册