写在前面

  AutoMapper目录:

  本篇目录:

  随着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,但是有时候在一些复杂的业务场景中,可能会存在跨类型映射转换,比如我们下面的场景:

         public class Source
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
public class Destination
{
public int Value1 { get; set; }
public DateTime Value2 { get; set; }
public Type Value3 { get; set; }
}

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

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

         //
// 摘要:
// Skip member mapping and use a custom type converter instance to convert to
// the destination type
//
// 类型参数:
// TTypeConverter:
// Type converter type
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
//
// 摘要:
// Skip member mapping and use a custom function to convert to the destination
// type
//
// 参数:
// mappingFunction:
// Callback to convert from source type to destination type
void ConvertUsing(Func<TSource, TDestination> mappingFunction);
//
// 摘要:
// Skip member mapping and use a custom type converter instance to convert to
// the destination type
//
// 参数:
// converter:
// Type converter instance
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);

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

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

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

         public class DateTimeTypeConverter : TypeConverter<string, DateTime>
{
protected override DateTime ConvertCore(string source)
{
return System.Convert.ToDateTime(source);
}
}
public class TypeTypeConverter : TypeConverter<string, Type>
{
protected override Type ConvertCore(string source)
{
TypeConverter dd = TypeDescriptor.GetConverter(source.GetType());
dd.CanConvertTo(typeof(Type));
Type type = Assembly.GetExecutingAssembly().GetType(source);
return type;
}
}

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

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

  AutoMapper配置转换代码:

         public void Example()
{
var source = new Source
{
Value1 = "",
Value2 = "01/01/2000",
Value3 = "DTO_AutoMapper使用详解.GlobalTypeConverters+Destination"
}; // 配置 AutoMapper
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);
Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
Mapper.CreateMap<Source, Destination>();
Mapper.AssertConfigurationIsValid(); // 执行 mapping
Destination result = Mapper.Map<Source, Destination>(source);
Console.WriteLine("result.Value1:" + result.Value1.ToString());
Console.WriteLine("result.Value2:" + result.Value2.ToString());
Console.WriteLine("result.Value3:" + result.Value3.ToString());
}

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

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

Custom Value Resolvers-自定义值解析器

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

         public class Source
{
public int Value1 { get; set; }
public int Value2 { get; set; }
}
public class Destination
{
public int Total { get; set; }
}

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

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

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

         //
// 摘要:
// Resolve destination member using a custom value resolver
//
// 类型参数:
// TValueResolver:
// Value resolver type
//
// 返回结果:
// Value resolver configuration options
IResolverConfigurationExpression<TSource, TValueResolver> ResolveUsing<TValueResolver>() where TValueResolver : IValueResolver;
//
// 摘要:
// Resolve destination member using a custom value resolver callback. Used instead
// of MapFrom when not simply redirecting a source member Access both the source
// object and current resolution context for additional mapping, context items
// and parent objects This method cannot be used in conjunction with LINQ query
// projection
//
// 参数:
// resolver:
// Callback function to resolve against source type
void ResolveUsing(Func<ResolutionResult, object> resolver);
//
// 摘要:
// Resolve destination member using a custom value resolver callback. Used instead
// of MapFrom when not simply redirecting a source member This method cannot
// be used in conjunction with LINQ query projection
//
// 参数:
// resolver:
// Callback function to resolve against source type
void ResolveUsing(Func<TSource, object> resolver);

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

         public class CustomResolver : ValueResolver<Source, int>
{
protected override int ResolveCore(Source source)
{
return source.Value1 + source.Value2;
}
}

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

         public void Example()
{
var source = new Source
{
Value1 = ,
Value2 =
}; // 配置 AutoMapper
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total, opt => opt.ResolveUsing<CustomResolver>());
Mapper.AssertConfigurationIsValid();
// 执行 mapping
var result = Mapper.Map<Source, Destination>(source); Console.WriteLine("result.Total:" + result.Total);
}

  转换效果:

  除了上述使用方式,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容器来构建,转换效果和上面那种方式一样,调用示例:

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

Null Substitution-空值替换

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

         public class Source
{
public string Value { get; set; }
}
public class Destination
{
public string Value { get; set; }
}
public void Example()
{
var source = new Source { Value = null }; // 配置 AutoMapper
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Value, opt => opt.NullSubstitute("Other Value"));
Mapper.AssertConfigurationIsValid();
// 执行 mapping
var result = Mapper.Map<Source, Destination>(source);
Console.WriteLine("result.Value:" + result.Value); source.Value = "Not null";
result = Mapper.Map<Source, Destination>(source);
Console.WriteLine("result.Value:" + result.Value);
}

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

Containers-IoC容器

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

  应改为:

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

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

     public class InversionOfControl
{
private class Source
{
public int Value { get; set; }
}
private class Destination
{
public int Value { get; set; }
}
public void Example2()
{
//StructureMap初始化,添加配置命令
ObjectFactory.Initialize(init =>
{
init.AddRegistry<MappingEngineRegistry>();
}); Mapper.Reset(); var configuration = ObjectFactory.GetInstance<IConfiguration>();//返回注册类型为IConfiguration的对象
configuration.CreateMap<Source, Destination>();//相当于Mapper.CreateMap var engine = ObjectFactory.GetInstance<IMappingEngine>();//返回注册类型为IMappingEngine的对象 var destination = engine.Map<Source, Destination>(new Source { Value = });//相当于Mapper.Map
Console.WriteLine("destination.Value:" + destination.Value);
}
} public class MappingEngineRegistry : Registry
{
public MappingEngineRegistry()
{
ForRequestedType<IConfiguration>()
.TheDefault.Is.ConstructedBy(() => Mapper.Configuration); ForRequestedType<IMappingEngine>()
.TheDefault.Is.ConstructedBy(() => Mapper.Engine);
}
}

  代码就这么多,但是可以简单体现出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的使用先整理这些,后面会陆续更新,还请关注。

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

  参考资料:

【AutoMapper官方文档】DTO与Domin Model相互转换(中)的更多相关文章

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

    写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...

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

    写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...

  3. 一起学微软Power BI系列-官方文档-入门指南(3)Power BI建模

    我们前2篇文章:一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍 和一起学微软Power BI系列-官方文档-入门指南(2)获取源数据 中,我们介绍了官方入门文档与获取 ...

  4. Akka Typed 官方文档之随手记

    ️ 引言 近两年,一直在折腾用FP与OO共存的编程语言Scala,采取以函数式编程为主的方式,结合TDD和BDD的手段,采用Domain Driven Design的方法学,去构造DDDD应用(Dom ...

  5. Kotlin开发语言文档(官方文档)-- 目录

    开始阅读Kotlin官方文档.先上文档目录.有些内容还未阅读,有些目录标目翻译还需琢磨琢磨.后续再将具体内容的链接逐步加上. 文档链接:https://kotlinlang.org/docs/kotl ...

  6. Spring 4 官方文档学习(十二)View技术

    关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

  7. Spring 4 官方文档学习(十一)Web MVC 框架之HTTP caching support

    一个良好的HTTP缓存策略可以显著地增进web应用的性能和其客户端的体验.主要使用"Cache-Control" HTTP response header来完成,配合conditi ...

  8. Swift -- 官方文档Swift-Guides的学习笔记

    在经历的一段时间的郁闷之后,我发现感情都是虚伪的,只有代码是真实的(呸) 因为看了swift语法之后依然不会用swift,然后我非常作死的跑去看官方文档,就是xcode里自带的help>docu ...

  9. [dpdk] 读官方文档(1)

    前提:已读了这本书<<深入浅出dpdk(朱清河等著)>>. 目标:读官方文档,同时跟着文档进行安装编译等工作. http://dpdk.org/doc/guides/index ...

随机推荐

  1. 获取微软原版“Windows 10 推送器(GWX)” 卸载工具

    背景: 随着Windows 10 免费更新的结束,针对之前提供推送通知的工具(以下简称GWX)来说使命已经结束,假设您还未将Windows 8.1 和Windows 7 更新到Windows 10 的 ...

  2. gulp初学

    原文地址:gulp初学 至于gulp与grunt的区别,用过的人都略知一二,总的来说就是2点: 1.gulp的gulpfile.js  配置简单而且更容易阅读和维护.之所以如此,是因为它们的工作方式不 ...

  3. VS2015在创建项目时的一些注意事项

    一.下面是在创建一个新的项目是我最常用的,现在对他们一一做一个详细的介绍: 1.Win32控制台应用程序我平时编写小的C/C++程序都用它,它应该是用的最多的. 2.名称和解决方案名称的区别:名称是项 ...

  4. 深入理解css3中nth-child和 nth-of-type的区别

    在css3中有两个新的选择器可以选择父元素下对应的子元素,一个是:nth-child 另一个是:nth-of-type. 但是它们到底有什么区别呢? 其实区别很简单::nth-of-type为什么要叫 ...

  5. 编译器开发系列--Ocelot语言2.变量引用的消解

    "变量引用的消解"是指确定具体指向哪个变量.例如变量"i"可能是全局变量i,也可能是静态变量i,还可能是局部变量i.通过这个过程来消除这样的不确定性,确定所引用 ...

  6. 仿陌陌的ios客户端+服务端源码项目

    软件功能:模仿陌陌客户端,功能很相似,注册.登陆.上传照片.浏览照片.浏览查找附近会员.关注.取消关注.聊天.语音和文字聊天,还有拼车和搭车的功能,支持微博分享和查找好友. 后台是php+mysql, ...

  7. 如何开发FineReport的自定义控件?

    FineReport作为插件化开发的报表软件,有些特殊需求的功能需要自己开发,开发的插件包帆软官方有提提供,可以去帆软论坛上找,本文将主要介绍如何开发一个自定义控件,这里讲讲方法论. 第一步:实例化一 ...

  8. pycharm2016.3.1激活及汉化

    pycharm快捷键 PyCharm设置python新建文件指定编码为utf-8 Python | 设置PyCharm支持中文 0, 注册码 43B4A73YYJ-eyJsaWNlbnNlSWQiOi ...

  9. Asp.Net跨平台:Ubuntu14.0+Mono+Jexus+Asp.Net

    Asp.Net跨平台的文章园子里有很多,这里给自己搭建的情况做一下总结,方便以后查看. 参考网站:   http://www.linuxdot.net/(Linux DotNET大本营 )  http ...

  10. 使用C# 和Consul进行分布式系统协调

    随着大数据时代的到来,分布式是解决大数据问题的一个主要手段,随着越来越多的分布式的服务,如何在分布式的系统中对这些服务做协调变成了一个很棘手的问题.今天我们就来看看如何使用C# ,利用开源对分布式服务 ...