写在前面
AutoMapper目录:
- 【AutoMapper官方文档】DTO与Domin Model相互转换(上)
- 【AutoMapper官方文档】DTO与Domin Model相互转换(中)
- 【AutoMapper官方文档】DTO与Domin Model相互转换(下)
- 未完待续。。。
本篇目录:
- 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,但是有时候在一些复杂的业务场景中,可能会存在跨类型映射转换,比如我们下面的场景:
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的使用先整理这些,后面会陆续更新,还请关注。
如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^
参考资料:
- https://github.com/AutoMapper/AutoMapper/wiki
- http://www.cnblogs.com/dudu/archive/2011/12/16/2284828.html
- http://www.cnblogs.com/ego/archive/2009/05/13/1456363.html
- http://www.cnblogs.com/jiguixin/archive/2011/09/19/2181521.html
- http://blog.csdn.net/yujunwu2525/article/details/7850486
【AutoMapper官方文档】DTO与Domin Model相互转换(中)的更多相关文章
-
【AutoMapper官方文档】DTO与Domin Model相互转换(上)
写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...
-
【AutoMapper官方文档】DTO与Domin Model相互转换(下)
写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...
-
一起学微软Power BI系列-官方文档-入门指南(3)Power BI建模
我们前2篇文章:一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍 和一起学微软Power BI系列-官方文档-入门指南(2)获取源数据 中,我们介绍了官方入门文档与获取 ...
-
Akka Typed 官方文档之随手记
️ 引言 近两年,一直在折腾用FP与OO共存的编程语言Scala,采取以函数式编程为主的方式,结合TDD和BDD的手段,采用Domain Driven Design的方法学,去构造DDDD应用(Dom ...
-
Kotlin开发语言文档(官方文档)-- 目录
开始阅读Kotlin官方文档.先上文档目录.有些内容还未阅读,有些目录标目翻译还需琢磨琢磨.后续再将具体内容的链接逐步加上. 文档链接:https://kotlinlang.org/docs/kotl ...
-
Spring 4 官方文档学习(十二)View技术
关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...
-
Spring 4 官方文档学习(十一)Web MVC 框架之HTTP caching support
一个良好的HTTP缓存策略可以显著地增进web应用的性能和其客户端的体验.主要使用"Cache-Control" HTTP response header来完成,配合conditi ...
-
Swift -- 官方文档Swift-Guides的学习笔记
在经历的一段时间的郁闷之后,我发现感情都是虚伪的,只有代码是真实的(呸) 因为看了swift语法之后依然不会用swift,然后我非常作死的跑去看官方文档,就是xcode里自带的help>docu ...
-
[dpdk] 读官方文档(1)
前提:已读了这本书<<深入浅出dpdk(朱清河等著)>>. 目标:读官方文档,同时跟着文档进行安装编译等工作. http://dpdk.org/doc/guides/index ...
随机推荐
-
初学者 bootstrap(二)----在路上(4)
---------------------------------------------------------曾经认真书写的笔记啊,别来无恙啊--------------------------- ...
-
jquery validate 在ajax提交表单下的验证方法
$(function() { var method='${method }'; if(method == 'edit'){ url="${ctx}/commodity/typeReN ...
-
Keil C51软件的使用
进入 Keil C51 后,屏幕如下图所示.几秒钟后出现编辑界 启动Keil C51时的屏幕 进入Keil C51后的编辑界面 简单程序的调试:学习程序设计语言.学习某种程序软件,最好的方法是直接操作 ...
-
转:昨天去参加adobe AIR发布会
昨天去参加adobe AIR发布会 2008-03-05 12:23 12547人阅读 评论(33) 收藏 举报 adobeairsliverlightwpf微软互联网 首先申明:我不是adobe雇佣 ...
-
远程连接身份验证错误,又找不到加密Oracle修正
远程连接服务器出现了错误,错误信息为:远程连接身份验证错误,又找不到加密Oracle修正. 服务器系统:Windows Server2016 客户端系统:Windows10家庭版和专业版 出错原因 ...
-
证明与计算(4): 完美散列函数(Perfect Hash function)
原文:wiki: 完美散列函数 假设,写一个SQL语句解析器,词法分析对SQL语句解析,把语句分成了多个token,一般这个时候会需要查询这个token是否是一个关键字token. 例如keyword ...
-
docker搭建mysql
下载mysql镜像 [root@localhost ~]# docker pull mysql: 创建mysql容器 [root@localhost ~]# docker run -itd --nam ...
-
C++ 复习要点、面试常见问题总结
本文总结一下C++面试时常遇到的问题.C++面试中,主要涉及的考点有: 关键字极其用法,常考的关键字有const, sizeof, typedef, inline, static, extern, n ...
-
Oracle 多表查询、查询运算符和集合运算
一.多表查询 1.内连接 一般使用INNER JOIN关键字指定内连接,INNER可以省略,默认表示内连接.查询结果中只包含两表的公共字段值相等的行,列可以是两表中的任意列 2.外连接 包括左外连接. ...
-
httpclient使用用例
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import jav ...