.NET AutoMapper学习记录

在两个不一样的类型对象之间传输数据,一般咱们会用DTOs(数据传输对象),AutoMapper就是将一个对象自动转换为另外一个对象的技术html

背景

一些orm框架,在用到Entity的时候有一些开源代码用到了automapper(如:nopcommence),将数据对象转成DTO。好比在ORM中,与数据库交互用的Model模型是具备不少属性变量方法神马的。而当咱们与其它系统(或系统中的其它结构)进行数据交互时,出于耦合性考虑或者安全性考虑或者性能考虑(总之就是各类考虑),咱们不但愿直接将这个Model模型传递给它们,这时咱们会建立一个贫血模型来保存数据并传递。什么是贫血模型?贫血模型(DTO,Data Transfer Object)就是说只包含属性什么的,只能保存必须的数据,没有其它任何的多余的方法数据什么的,专门用于数据传递用的类型对象。在这个建立的过程当中,若是咱们手动来进行,就会看到这样的代码:前端

A a=new A();
a.X1=b.X1;
a.X2=b.X2;
...
...
...
return a; 太麻烦数据库

此时,AutoMapper能够发挥的做用就是根据A的模型和B的模型中的定义,自动将A模型映射为一个全新的B模型。(不用一个属性一个属性的赋值)后端

好处:安全

一、 db或者模型 增长字段时,只需在DTO内部增长映射,赋值代码无需修改app

二、隔离,前端收集各参数,不用管后端定义的模型。先后端才用AutoMapper来作转换。框架

使用

Nuget引用:AutoMapper    版本不同,里面的不少方法有些不同ide

AutoMapper是基于约定的,所以在实用映射以前,咱们须要先进行映射规则的配置。工具

咱们要作的只是将要映射的两个类型告诉AutoMapper(调用Mapper类的Static方法CreateMap并传入要映射的类型): 性能

Mapper.Initialize(cfg => { cfg.CreateMap<StudentEntity, StudentOutput>(); });

 

也能够将实体类 放在配置文件MapperProfile中

Mapper.Initialize(cfg => {

cfg.AddProfile<MapperProfile>();

cfg.AddProfile<ProxyAdapterProfile>();  //可增长多个

});

注意:屡次调用 Mapper.Initialize() 只有最后一次生效。因此只能用一个Mapper.Initialize

【AutoMapper.7.0.1】

class MapperProfile : Profile

  {

public MapperProfile()

        {

            CreateMap<StudentEntity, StudentOutput>();

            var map = CreateMap<UploadResponseBase, UploadResult>();

            //字段名称不一致,一次直接定义好全部字段的映射规则

            map.ConvertUsing(s => new UploadResult

            {

                IsSuccess = s.success,

                FileUrl = s.clientUrl,

                ErrorMessage = s.rawFileName

                , datetimeStr = (s.datetime).ToString(),

            });

        }

}
View Code

【AutoMapper 4.2.1.0】

AutoMapper使用ForMember来指定每个字段的映射规则:

protected override void Configure()

        {

            var mapResponst = CreateMap<Response, CResponse>();

            mapResponst.ForMember(dest => dest.departure_date, opt => opt.MapFrom(src => src.DepartureDate.ToString("yyyy-MM-dd HH:mm:ss")))

                                  .ForMember(dest => dest.ticket_price, opt => opt.MapFrom(src => src.TicketPrice));

 

            var mapContacts = CreateMap<CContacts, PassengerInputEntity>();

            mapContacts.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.First_Name))

            .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.Last_Name))

            .ForMember(dest => dest.AreaCode, opt => opt.MapFrom(src => src.Area_Code));

        }
View Code

而后就能够交给AutoMapper帮咱们搞定一切了: 

//实例化实体List

            List<StudentEntity> StudentList = new List<StudentEntity>();

            //模拟数据

            StudentList.Add(new StudentEntity

            {

                Id = 1,

                Age = 12,

                Gander = "boy",

                Name = "WangZeLing",

                Say = "Only the paranoid survive",

                Score = 99M

            });

       //AuotMapper具体使用方法 将List<StudentEntity>转换为List<StudentOutput>

            List<StudentOutput> Output = Mapper.Map<List<StudentOutput>>(StudentList);

            Output.ForEach(output => Console.WriteLine(string.Format("name:{0},say:{1},score:{2}", output.Name, output.Say, output.Score)));

解释

一、 AutoMapper给咱们提供的Convention或Configuration方式并非“异或的”,咱们能够结合使用两种方式,为名称不一样的字段配置映射规则,而对于名称相同的字段则忽略配置。

二、 在映射具备相同字段名的类型时,会自动转换

三、 不相同名称的属性则须要 指定映射字段,设置ConvertUsing或者ForMember..

四、 值为空的属性,AutoMapper在映射的时候会把相应属性也置为空       

五、 若是传入一个空的AddressDto,AutoMapper也会帮咱们获得一个空的Address对象。 

Address address = Mapper.Map<AddressDto,Address>(null); 

六、不须要映射的属性能够用Ignore忽略。【if有验证 目标类中的全部属性是否都被映射 时】

使用Ignore方法:

Mapper.CreateMap<Entity.Source, Entity.Destination>()

    .ForMember(dest => dest.SomeValuefff, opt =>

    {

        opt.Ignore();

    });

最佳实践

这段内容将讨论AutoMapper的规则写在什么地方的问题。

在上一段中,咱们已经知道了如何使用AutoMapper进行简单的对象映射,可是,在实际的项目中,咱们会有不少类进行映射(从Entity转换为Dto,或者从Entity转换为ViewModel等),这么多的映射如何组织将成为一个问题。

首先咱们须要定义一个Configuration.cs的类,该类提供AutoMapper规则配置的入口,它只提供一个静态的方法,在程序第一次运行的时候调用该方法完成配置。

当有多个Profile的时候,咱们能够这样添加:

public class Configuration

{

    public static void Configure()

    {

        Mapper.Initialize(cfg =>

        {

            cfg.AddProfile<Profiles.SourceProfile>();

            cfg.AddProfile<Profiles.OrderProfile>();

            cfg.AddProfile<Profiles.CalendarEventProfile>();

        });

    }

}

在程序运行的时候,只须要调用Configure方法便可。

了解了这些实现之后,咱们能够再项目中添加AutoMapper文件夹。

 Configuration为咱们的静态配置入口类;Profiles文件夹为咱们全部Profile类的文件夹。若是是MVC,咱们须要在Global中调用:

AutoMapper.Configuration.Configure();

 

问题:Missing type map configuration or unsupported mapping

重现:本地调试直接打开出错的页面,调试发现是ok的;而后先打开用到了mapper所在控制器对应的页面,再去打开出错的页面,是报错的。

从 GitHub 上签出 AutoMapper 的源代码一看 Mapper.Initialize() 的实现,恍然大悟。

public static void Initialize(Action<IMapperConfigurationExpression> config)

{

    Configuration = new MapperConfiguration(config);

    Instance = new Mapper(Configuration);

}

原来每次调用 Mapper.Initialize() 都会建立新的 Mapper 实例,也就是屡次调用 Mapper.Initialize() 只有最后一次生效。

切记不要多处调用Mapper.Initialize()

 优化方法:【写一个工具类】

需程序集:AutoMapper

/// <summary>

    ///     优化AutoMap映射工具,解决AutoMap只能Initialize一次的问题

    /// </summary>

    public class AutoMapperManager

    {

        /// <summary>

        /// 存储全部的profile

        /// </summary>

        static ConcurrentBag<Profile> Profiles;

        static AutoMapperManager()

        {

            Profiles = new ConcurrentBag<Profile>();

        }

        /// <summary>

        /// 新增Profile,必須放在靜態構造函數里

        /// </summary>

        /// <param name="profile"></param>

        public static void AddProfile(Profile profile)

        {

            Profiles.Add(profile);

        }

        /// <summary>

        /// 初始化,能够屡次调用,同时以前的Profile也会生效

        /// </summary>

        public static void Initialize()

        {

            Mapper.Initialize(config =>

            {

                Profiles.ToList().ForEach(file =>

                {

                    config.AddProfile(file);

                });

            });

        }

    }
View Code

其余地方须要用mapper的地方 调用方式:

AutoMapperManager.AddProfile(new Profile1());

AutoMapperManager.AddProfile(new Profile2());

AutoMapperManager.Initialize();

 

参考:

http://www.javashuo.com/article/p-webbctkb-hd.html

https://www.cnblogs.com/youring2/p/automapper.html

http://www.cnblogs.com/dudu/p/5875579.html