近日在做项目的时候,遇到了个怪问题,关于AutoMapper的细节问题,也是不为一般人所关注的。
本人研究AutoMapper也没有多长时间,而且研究的过程中也写了关于AutoMapper的系列基础教程,但是毕竟AutoMapper是个开源项目,并不是一个简单的系列教程就能解释的清楚的,只能解释个大概,项目实战的时候,遇到的细节问题还得自己私下里再次研究、总结。
首先,我要说明的是,这篇博客的写作顺序是按事情发展的顺序来写的,也就是说,在我想写这篇博客时,问题的根本原因还没找到,但是此时,我回过头来,再来看这个过程中每个问题的解决思路,我想,此时这个细节的问题已经清楚了,也来分享一下。
先来让大家看看我的AutoMapper的大致配置过程:
- 先创建实体类对应的AutoMapper配置类,命名规范是EntityName(实体类名)+Profile,比如PersonProfile,ProvincePerfile等。
public class ProvinceProfile : Profile { protected override void Configure() { Mapper.Initialize(cfg => cfg.CreateMap<Provinces, ProvinceDto>() .ForSourceMember(src=>src.UpdatedDate,opt=>opt.Ignore()) .ReverseMap()); } }
- 再创建一个静态类,取名AutoMapperConfig,然后在他的静态方法中初始化Mapper,并添加所有的配置类,这里我还添加了其他配置类。
public static class AutoMapperWebConfig { public static void Configure() { Mapper.Reset(); Mapper.Initialize(cfg => { cfg.AddProfile<ProvinceProfile>(); cfg.AddProfile<CityProfile>(); cfg.AddProfile<StationProfile>(); cfg.AddProfile<TerminalDeviceProfile>();//TerminalDeviceProfile依赖ProvinceProfile cfg.AddProfile<OperatorProfile>(); }); Mapper.AssertConfigurationIsValid();//验证所有的映射配置是否都正常 } }
- 最后,在项目启动的时候(ASP.Net程序在.asax文件中的Application_Start方法)调用AutoMapperWebConfig.Configure();就可以了。
这一切都感觉这么顺利,但是往往越是顺利的时候,也意味着不顺快来了。接下来,类似下面截图中的错误向我狂轰乱炸。
当我使用在应用层使用Mapper.Map()方法将实体类映射为Dto类时,报错如下:
很明显这是AutoMapper映射错误。
接下来就各种搜索错误,找到了下面一篇博客,原文
ta的解决方案是,如图
看到这里,我很高兴,赶紧改了一下自己的代码,发现果然成功了!但是该博主并没有给出个所以然来。
但是,我不服气,我之前已经创建了两个类之间的映射啊,为啥Mapper.Map()方法不行,我就纳闷了,我非得搞清楚这两者之间的关系不可。
我寻思着,就字面意思来看,一个是“映射”,一个是“动态映射”,会有啥区别呢?
于是各种搜两者之间的区别,去StackOverflow上找到了下面的答案:原链接
这里说,DynamicMap在编译时你不知道源类型的情况下使用,那么,相应地,Map就是在编译时知道源类型的情况下使用。简单的这一句解释并不能让接触AutoMapper时间不长的人有所启示。
而问题就出在,我之前已经创建了映射,所以在编译时应该可以确定源类型的,更何况我这两个类都很简单,不可能是因为数据类型不一致造成的映射失败啊!代码如下:
public class Provinces : Entity { public virtual string Code{ get; set; } public virtual string Name{ get; set; } public virtual string UpdatedBy{ get; set; } public virtual DateTime? UpdatedDate{ get; set; } public Provinces(){} }
public class ProvinceDto:EntityDto { public string Code { get; set; } public string Name { get; set; } }
于是,还得不到自己想要的答案,就去GitHub上AutoMapper的项目下Open了一个Issue,点击查看我提的Issue,在这里,我得到了我想要的答案。
该回答者给我的答案如上,他猜想我在每个实体类对应的Profile文件中,应该直接使用CreateMap,而不是在Mapper.Initialize中使用CreateMap。按照他的提示,我修改了代码,以后再也没有出现错误。
=============该总结了================
现在再次回头看看这个问题,完全是了然于胸的感觉。因为我在AutoMapperWebconfig静态类的静态方法中已经进行了Mapper.Initialize(),这是AutoMapper的初始化,而在每个实体类对应的Profile类中又使用了一次。尝试着去想一想,第一次将所有的文配置类都初始化到Mapper(暂且先将它理解成一个容器)中,第二次调用Mapper.Initialize()可能会把之前的内容都擦除掉,所以使用Mapper.Map的时候会报错就可以想明白了,而使用DynamicMap,之前在哪里看到过DynamicMap就想当于先CreateMap,再Map,所以,我们之前的配置就可有可无,无关紧要了,因为DynamicMap会把之前的配置擦除掉,所以上面截图中的映射成功了。而至于其他配置类没有出现该错误,该回答者给的答案是”这可能是个意外!“。在这次坎坷曲折的解决问题的过程中,还是学到了很多东西的。在这里,我们也很显然,可以看出博客园那位园友使用DynamicMap成功后而没给出为什么,十有八九也是我这个问题了。
==============总结完毕================