本篇目录:

  • Custom Type Converters-自定义类型转换器
  • Custom Value Resolvers-自定义值解析器
  • Null Substitution-空值替换
  • Containers-IoC容器
  • 后记

  随着AutoMapper的学习深入,发现AutoMapper
在对象转换方面(Object-Object Mapping)还蛮强大的,当时使用AutoMapper的场景是DTO与Domin
Model相互转换,所以文章的标题就是这个(标题有误),其实AutoMapper不止在这方面的转换,应该是涵盖所有对象(Object)之间的转
换,上篇主要讲AutoMapper的基本转换使用,本篇可以定义为AutoMapper的灵活配置篇。

  插一句:有时候学习一门技术,还没有学很深入,发现还有比其更好的,然后就去学习另外一门技术,可能到头来什么也没学会、学精,前两天看一篇C#程序员-你为何不受大公司青睐,其实不是C#不好,而是你没有学好,就像前几年讨论C#和Java哪个好一样,我个人觉得去争论这些很是无聊,还不如静下心,好好的学好一门技术,那就是成功的。

Custom Type Converters-自定义类型转换器

  在上篇中,我们使用AutoMapper类型映射转换,都是相同的类型转换,比如string转string、datetime转datetime,但是有时候在一些复杂的业务场景中,可能会存在跨类型映射转换,比如我们下面的场景:

 1         public class Source
 2         {
 3             public string Value1 { get; set; }
 4             public string Value2 { get; set; }
 5             public string Value3 { get; set; }
 6         }
 7         public class Destination
 8         {
 9             public int Value1 { get; set; }
10             public DateTime Value2 { get; set; }
11             public Type Value3 { get; set; }
12         }

  从代码中可以看出,string需要转换为目标类型:int、DateTime和Type,转换的类型并不一致,虽然我们命名符合PascalCase命名规则,但是如果还是按照正常的类型映射转换就会报下面异常:

  ”AutoMapperMappingException“这个异常我们在上篇中提到多次,AutoMapper在类型映射的时候,如果找不到或映射类型不一致都会报这个异常,怎么办?天无绝人之路,AutoMapper提供了自定义类型转换器:

 1         //
 2         // 摘要:
 3         //     Skip member mapping and use a custom type converter instance to convert to
 4         //     the destination type
 5         //
 6         // 类型参数:
 7         //   TTypeConverter:
 8         //     Type converter type
 9         void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
10         //
11         // 摘要:
12         //     Skip member mapping and use a custom function to convert to the destination
13         //     type
14         //
15         // 参数:
16         //   mappingFunction:
17         //     Callback to convert from source type to destination type
18         void ConvertUsing(Func<TSource, TDestination> mappingFunction);
19         //
20         // 摘要:
21         //     Skip member mapping and use a custom type converter instance to convert to
22         //     the destination type
23         //
24         // 参数:
25         //   converter:
26         //     Type converter instance
27         void ConvertUsing(ITypeConverter<TSource, TDestination> converter);

  ConvertUsing是什么?它是我们在指定类型映射时,类型配置的方法,就是说通过ConvertUsing方法把类型映射中类型转换的权限交给用户配置,而不是通过AutoMapper进行自动类型转换,这就给我提供了更多的自定义性,也就避免了不同类型之间转换而引起的”AutoMapperMappingException“异常。

  ConvertUsing三个重载方 法,第二个适用于简单类型转换,接受一个类型,返回一个目标类型,第一个和第三个其实基本一样,一个是实例,一个是类型,但都必须是 ITypeConverter<TSource, TDestination>泛型接口的派生类。

  正好上面示例中需要对三种类型进行转换,就分别用ConvertUsing三个重载方法,因为string转int很简单就使用第二个重载方法,string转DateTime、Type自定义类型转换器:

 1         public class DateTimeTypeConverter : TypeConverter<string, DateTime>
 2         {
 3             protected override DateTime ConvertCore(string source)
 4             {
 5                 return System.Convert.ToDateTime(source);
 6             }
 7         }
 8         public class TypeTypeConverter : TypeConverter<string, Type>
 9         {
10             protected override Type ConvertCore(string source)
11             {
12                 TypeConverter dd = TypeDescriptor.GetConverter(source.GetType());
13                 dd.CanConvertTo(typeof(Type));//返回false
14                 Type type = Assembly.GetExecutingAssembly().GetType(source); //获取类型,但是赋值不了给Type,所以type=null
15                 return type;
16             }
17         }

  当然业务场景如果复杂的话,可以在转换器中做些详细的配置内容,TypeConverter的CanConvertTo方法是判断相互转换类型的可行性,不可转换返回false,除此之外,再列下TypeConverter的几个常用方法:

CanConvertFrom(Type) 返回该转换器是否可以将给定类型的对象转换为此转换器的类型。
CanConvertFrom(ITypeDescriptorContext, Type) 返回该转换器是否可以使用指定的上下文将给定类型的对象转换为此转换器的类型。
CanConvertTo(Type) 返回此转换器是否可将该对象转换为指定的类型。
CanConvertTo(ITypeDescriptorContext, Type) 返回此转换器是否可以使用指定的上下文将该对象转换为指定的类型。
ConvertFrom(Object) 将给定值转换为此转换器的类型。
ConvertFrom(ITypeDescriptorContext, CultureInfo, Object) 使用指定的上下文和区域性信息将给定的对象转换为此转换器的类型。

  AutoMapper配置转换代码:

 1         public void Example()
 2         {
 3             var source = new Source
 4             {
 5                 Value1 = "5",
 6                 Value2 = "01/01/2000",
 7                 Value3 = "DTO_AutoMapper使用详解.GlobalTypeConverters+Destination"
 8             };
 9
10             // 配置 AutoMapper
11             Mapper.CreateMap<string, int>().ConvertUsing((string s) => { return Convert.ToInt32(s); });
12             Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
13             Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
14             Mapper.CreateMap<Source, Destination>();
15             Mapper.AssertConfigurationIsValid();
16
17             // 执行 mapping
18             Destination result = Mapper.Map<Source, Destination>(source); //完成最终映射
19             Console.WriteLine("result.Value1:" + result.Value1.ToString());
20             Console.WriteLine("result.Value2:" + result.Value2.ToString());
21             Console.WriteLine("result.Value3:" + result.Value3.ToString()); //输出错误,因为自定义的转换器TypeTypeConverter里面转不成功
22         }

  在自定义转换配置中虽然配置了转换类型,但是CreateMap中也需要制定其类型,而且要和转换器中类型所一致,最后Mapper.CreateMap<Source, Destination>();完成Source到Destination的配置转换,其实上面的配置器可以看成Source(原始类型)和Destination(目标类型)所依赖类型之间的转换。转换效果:

  自定义转换配置器的强大之处在于,我们可以完成任何类型之间的相互转换(只要符合CanConvertTo),因为类型转换我们说了算,在业务场景中,我们可以定义一组自定义转换配置器,这样就不需要再做额外的配置,就可以完成想要的类型转换。

Custom Value Resolvers-自定义值解析器

  上面讲了自定义类型转换器,针对的是不同类型之间映射处理,有这样一种场景: 领域模型到DTO的转换,DTO并不是和领域模型之间完全一样,而且还要根据具体的业务场景做一些处理,什么意思?比如我们要对DTO做一些测试或其他一 些数据操作(如记录日志时间等),但是和业务无关,如果把这种操作放在领域模型中就有点不伦不类了,所以要在DTO转换中去做,比如下面场景:

1         public class Source
2         {
3             public int Value1 { get; set; }
4             public int Value2 { get; set; }
5         }
6         public class Destination
7         {
8             public int Total { get; set; }
9         }

  转换目标对象中我们想得到一个计算值,就是在转换中对目标值进行解析,如果你看了Projection这一节点,可能觉得很简单,我们可以使用自定义转换规则就可以做到:

1 Mapper.CreateMap<Source, Destination>()
2                 .ForMember(dest => dest.Total, opt => opt.MapFrom(src => src.Value1 + src.Value2));

  这种方式虽然可以解决上述场景中的问题,但是不提倡这样做,如果解析过程复杂一些,或者解析方式经常出现改动,这样我们维护起来就很麻烦了,所以我们要定义一个值解析器,或者称为目标值解析器,和上面说的类型转换器(ConvertUsing)比较类似,AutoMapper提供了ResolveUsing方法用于目标值解析器:

 1         //
 2         // 摘要:
 3         //     Resolve destination member using a custom value resolver
 4         //
 5         // 类型参数:
 6         //   TValueResolver:
 7         //     Value resolver type
 8         //
 9         // 返回结果:
10         //     Value resolver configuration options
11         IResolverConfigurationExpression<TSource, TValueResolver> ResolveUsing<TValueResolver>() where TValueResolver : IValueResolver;
12         //
13         // 摘要:
14         //     Resolve destination member using a custom value resolver callback. Used instead
15         //     of MapFrom when not simply redirecting a source member Access both the source
16         //     object and current resolution context for additional mapping, context items
17         //     and parent objects This method cannot be used in conjunction with LINQ query
18         //     projection
19         //
20         // 参数:
21         //   resolver:
22         //     Callback function to resolve against source type
23         void ResolveUsing(Func<ResolutionResult, object> resolver);
24         //
25         // 摘要:
26         //     Resolve destination member using a custom value resolver callback. Used instead
27         //     of MapFrom when not simply redirecting a source member This method cannot
28         //     be used in conjunction with LINQ query projection
29         //
30         // 参数:
31         //   resolver:
32         //     Callback function to resolve against source type
33         void ResolveUsing(Func<TSource, object> resolver);

  可以看到ResolveUsing方法和ConvertUsing方式比较类似,ResolveUsing方法参数对象必须是抽象类ValueResolver<TSource, TDestination>的派生类,准确的说是接口IValueResolver的派生类,和自定义转换器一样,我们要自定义一个目标值解析器:

1         public class CustomResolver : ValueResolver<Source, int>
2         {
3             protected override int ResolveCore(Source source)
4             {
5                 return source.Value1 + source.Value2;
6             }
7         }

  CustomResolver目标值解析器继承ValueResolver,指定源类型和目标值类型,并重写ResolveCore抽象方法,返回操作值,AutoMapper配置映射类型转换:

 1         public void Example()
 2         {
 3             var source = new Source
 4             {
 5                 Value1 = 5,
 6                 Value2 = 7
 7             };
 8
 9             // 配置 AutoMapper
10             Mapper.CreateMap<Source, Destination>()
11                 .ForMember(dest => dest.Total, opt => opt.ResolveUsing<CustomResolver>());
12             Mapper.AssertConfigurationIsValid();
13             // 执行 mapping
14             var result = Mapper.Map<Source, Destination>(source);
15
16             Console.WriteLine("result.Total:" + result.Total);
17         }

  转换效果:

  除了上述使用方式,AutoMapper还提供了自定义构造方法方式,英文原文:“Because we only supplied the type of the custom resolver to AutoMapper, the mapping engine will use reflection to create an instance of the value resolver.If we don't want AutoMapper to use reflection to create the instance, we can either supply the instance directly, or use the ConstructedBy method to supply a custom constructor method.AutoMapper will execute this callback function instead of using reflection during the mapping operation, helpful in scenarios where the resolver might have constructor arguments or need to be constructed by an IoC container.”

  就像上述这段话的最后,我理解的这种方式适用于自定义解析器中存在构造方法参数,或者通过IoC容器来构建,转换效果和上面那种方式一样,调用示例:

1             // 配置 AutoMapper
2             Mapper.CreateMap<Source, Destination>()
3                 .ForMember(dest => dest.Total,
4                            opt => opt.ResolveUsing<CustomResolver>().ConstructedBy(() => new CustomResolver())
5                 );

Null Substitution-空值替换

  空值替换,顾名思义就是原始值为空,在转换配置中我们定义替换空值的值,使用NullSubstitute方法,使用方式类似于Ignore方法,只不过Ignore是忽略或不包含的意思,NullSubstitute是为空赋值,接受一个object类型的参数,就是我们要指定替换的值,使用很简单,贴下示例代码:

 1         public class Source
 2         {
 3             public string Value { get; set; }
 4         }
 5         public class Destination
 6         {
 7             public string Value { get; set; }
 8         }
 9         public void Example()
10         {
11             var source = new Source { Value = null };
12
13             // 配置 AutoMapper
14             Mapper.CreateMap<Source, Destination>()
15                 .ForMember(dest => dest.Value, opt => opt.NullSubstitute("Other Value"));
16             Mapper.AssertConfigurationIsValid();
17             // 执行 mapping
18             var result = Mapper.Map<Source, Destination>(source);
19             Console.WriteLine("result.Value:" + result.Value);
20
21             source.Value = "Not null";
22             result = Mapper.Map<Source, Destination>(source);
23             Console.WriteLine("result.Value:" + result.Value);
24         }

  第一次转换源值对象为null,我们指定替换null的值为“Other Value”,并打印出目标类型转换值,第二次转换源值对象为“Not null”,配置和第一次转换一样,并打印出目标类型转换值。转换效果:

Containers-IoC容器

  AutoMapper中扩展了关于IoC的应用,这样使得我们在项目中应用AutoMapper更加灵活多变,但适用于大型项目或者业务场景很复杂的情况下,简单的项目没必要这样做,关于IoC的相关知识可以参照:http://www.cnblogs.com/xishuai/p/3666276.html,AutoMapper提供了IoC应用的相关示例代码,但是有些错误,比如在InversionOfControl类文件第81行:

  应改为:

1             ForRequestedType<ConfigurationStore>()
2                 .CacheBy(InstanceScope.Singleton)
3                 .TheDefault.Is.OfConcreteType<ConfigurationStore>()
4                 .CtorDependency<IEnumerable<IObjectMapper>>().Is(expr => expr.ConstructedBy(() => MapperRegistry.Mappers));

  运行中还有几个错误,比如IoC配置出错,AutoMapper配置无效等,都是通过AutoMapper提供相关接口进行注入的,不知道是不是配置问题,以后可以再研究下,这边稍微整理了下,通过Mapper提供的实例进行注入,简单演示下AutoMapper中IoC的应用。

 1     public class InversionOfControl
 2     {
 3         private class Source
 4         {
 5             public int Value { get; set; }
 6         }
 7         private class Destination
 8         {
 9             public int Value { get; set; }
10         }
11         public void Example2()
12         {
13             //StructureMap初始化,添加配置命令
14             ObjectFactory.Initialize(init =>
15             {
16                 init.AddRegistry<MappingEngineRegistry>();
17             });
18
19             Mapper.Reset();
20
21             var configuration = ObjectFactory.GetInstance<IConfiguration>();//返回注册类型为IConfiguration的对象
22             configuration.CreateMap<Source, Destination>();//相当于Mapper.CreateMap
23
24             var engine = ObjectFactory.GetInstance<IMappingEngine>();//返回注册类型为IMappingEngine的对象
25
26             var destination = engine.Map<Source, Destination>(new Source { Value = 15 });//相当于Mapper.Map
27             Console.WriteLine("destination.Value:" + destination.Value);
28         }
29     }
30
31     public class MappingEngineRegistry : Registry
32     {
33         public MappingEngineRegistry()
34         {
35             ForRequestedType<IConfiguration>()
36                 .TheDefault.Is.ConstructedBy(() => Mapper.Configuration);
37
38             ForRequestedType<IMappingEngine>()
39                 .TheDefault.Is.ConstructedBy(() => Mapper.Engine);
40         }
41     }

  代码就这么多,但是可以简单体现出AutoMapper中IoC的应用,应用IoC使用的是StructureMap,源码地址:https://github.com/structuremap/structuremap,使用NuGet安装StructureMap命令:“Install-Package StructureMap”,也可以直接添加StructureMap.dll,除了StructureMap,我们也可以使用微软提供的Unity进行依赖注入,参考教程:http://www.cnblogs.com/xishuai/p/3670292.html

  上述示例中, 我们在IoC中添加了两个类型映射,IConfiguration对应 Mapper.Configuration(IConfiguration),IMappingEngine对应 Mapper.Engine(IMappingEngine),使用AddRegistry泛型方法在初始化的时候,注入类型映射关系,就像Unity中Configure加载配置文件一样,然后通过ObjectFactory.GetInstance方法解析出注入类型的具体对象,示例中使用AutoMapper默认的Configuration和Engine实例,分别继承于IConfiguration和IMappingEngine接口,通过继承其接口,我们还可以自定义Configuration和Engine,当然除了IConfiguration和IMappingEngine接口,AutoMapper还提供了其他的接口类型,比如IConfigurationProvider,IProfileConfiguration,IProfileExpression等等,都可以对其进行IoC管理。

  转换效果:

后记

  示例代码下载:http://pan.baidu.com/s/1kTwoALT

  贪多嚼不烂,关于AutoMapper的使用先整理这些,后面会陆续更新,还请关注。

  如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^

  参考资料:

DTO学习系列之AutoMapper(三)的更多相关文章

  1. DTO学习系列之AutoMapper(六)----EntityFramework和AutoMapper的婚后生活

    写在前面 我到底是什么? 越界的可怕 做好自己 后记 文章标题主要关键字:mapping DTOs to Entities,注意并不是“Entities to DTOs”,表示实体对象到DTO的转换, ...

  2. DTO学习系列之AutoMapper(四)

    本篇目录: Mapping Inheritance-映射继承 Queryable Extensions (LINQ)-扩展查询表达式 Configuration-配置 Conditional Mapp ...

  3. DTO学习系列之AutoMapper(五)----当EntityFramework爱上AutoMapper

    有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易,相处不易. 在DDD(领域驱动设计)中,使用AutoMapp ...

  4. DTO学习系列之AutoMapper(二)

    本篇目录: Flattening-复杂到简单 Projection-简单到复杂 Configuration Validation-配置验证 Lists and Array-集合和数组 Nested m ...

  5. DTO学习系列之AutoMapper(一)

    一.前言 DTO(Data Transfer Object)数据传输对象,注意关键字“数据”两个字,并不是对象传输对象(Object Transfer Object),所以只是传输数据,并不包含领域业 ...

  6. 音视频学习系列第(三)篇---wav文件的存储和解析

    音视频系列 什么是wav wav是一种无损的音频文件格式,wav文件有两部分,第一部分是文件头,记录一些重要的参数信息,如音频的采样率,通道数,数据位宽,第二部分是数据部分,数据部分可以是PCM,也可 ...

  7. RabbitMQ学习系列(三): C# 如何使用 RabbitMQ

    上一篇已经讲了Rabbitmq如何在Windows平台安装,还不了解如何安装的朋友,请看我前面几篇文章:RabbitMQ学习系列一:windows下安装RabbitMQ服务 , 今天就来聊聊 C# 实 ...

  8. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

  9. Deep Learning(深度学习)学习笔记整理系列之(三)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

随机推荐

  1. defer和async

    1.decument.wirte不能使用 2.<script src="text.js" type="text/javascript" defer=&qu ...

  2. 随笔: WC2016感想

    在某些时刻,我可以体会到非常复杂的情感,这种情感神秘的来源不能被描述.它非常的复杂.你无法分清,这种情感是来源于一个个神经元控制的情感系统的一时冲动,亦或是你如实地反馈了你所正在感知的外界. 但我曾在 ...

  3. noip2015运输计划

    二分+LCA+查分前缀和 #include<iostream> #include<cstring> #include<cstdio> #include<alg ...

  4. 如何用正则将多个空格看成一个空格结合spllit()方法将文本数据入库

    关键的代码和正则表达式在这里 while((line=br.readLine())!=null) { String[] data=new String[4]; data=line.split(&quo ...

  5. mysql索引之唯一索引

    mysql 的唯一索引一般用于不重复的字段,一般会把表中的id设为唯一索引,创建唯一索引的目的不是为了提高查询速度,而是为了避免数据重复,注意:唯一索引可以有多个,但是列值必须唯一,创建唯一索引使用关 ...

  6. Robot Framework自动化测试---元素定位

    不要误认为Robot framework 只是个web UI测试工具,更正确的理解Robot framework是个测试框架,之所以可以拿来做web UI层的自动化是国为我们加入了selenium2的 ...

  7. Effective Java Item4:Enforce noninstantiability with a private constructor

    Item4:Enforce noninstantiability with a private constructor 通过构造私有化,禁止对象被实例化. public class UtilClass ...

  8. WCF 基于Cookie的登录验证回传问题的解决

    参考资料: http://www.cnblogs.com/czcz1024/p/3333138.html http://megakemp.com/2009/02/06/managing-shared- ...

  9. BZOJ1108: [POI2007]天然气管道Gaz

    1108: [POI2007]天然气管道Gaz Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 410  Solved: 211[Submit][Sta ...

  10. BZOJ1662: [Usaco2006 Nov]Round Numbers

    1662: [Usaco2006 Nov]Round Numbers Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 147  Solved: 84[Sub ...