.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

相關文章
相關標籤/搜索