AutoMapper中的Map和DynamicMap——高手注重细节,思考和总结

近日在做项目的时候,遇到了个怪问题,关于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类时,报错如下:

E{A2JHI%`4A68KE]KR6I_}3

很明显这是AutoMapper映射错误。

接下来就各种搜索错误,找到了下面一篇博客,原文

ta的解决方案是,如图

image

看到这里,我很高兴,赶紧改了一下自己的代码,发现果然成功了!但是该博主并没有给出个所以然来。

L]%SFP7IHKNLMU9V(_K}}{S

但是,我不服气,我之前已经创建了两个类之间的映射啊,为啥Mapper.Map()方法不行,我就纳闷了,我非得搞清楚这两者之间的关系不可。

我寻思着,就字面意思来看,一个是“映射”,一个是“动态映射”,会有啥区别呢?

于是各种搜两者之间的区别,去StackOverflow上找到了下面的答案:原链接

image

这里说,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,在这里,我得到了我想要的答案。

image

该回答者给我的答案如上,他猜想我在每个实体类对应的Profile文件中,应该直接使用CreateMap,而不是在Mapper.Initialize中使用CreateMap。按照他的提示,我修改了代码,以后再也没有出现错误。

=============该总结了================

现在再次回头看看这个问题,完全是了然于胸的感觉。因为我在AutoMapperWebconfig静态类的静态方法中已经进行了Mapper.Initialize(),这是AutoMapper的初始化,而在每个实体类对应的Profile类中又使用了一次。尝试着去想一想,第一次将所有的文配置类都初始化到Mapper(暂且先将它理解成一个容器)中,第二次调用Mapper.Initialize()可能会把之前的内容都擦除掉,所以使用Mapper.Map的时候会报错就可以想明白了,而使用DynamicMap,之前在哪里看到过DynamicMap就想当于先CreateMap,再Map,所以,我们之前的配置就可有可无,无关紧要了,因为DynamicMap会把之前的配置擦除掉,所以上面截图中的映射成功了。而至于其他配置类没有出现该错误,该回答者给的答案是”这可能是个意外!“。在这次坎坷曲折的解决问题的过程中,还是学到了很多东西的。在这里,我们也很显然,可以看出博客园那位园友使用DynamicMap成功后而没给出为什么,十有八九也是我这个问题了。

==============总结完毕================

posted @ 2015-11-27 19:19 tkbSimplest 阅读(...) 评论(...) 编辑 收藏