對象映射工具AutoMapper介紹

AutoMapper是用來解決對象之間映射轉換的類庫。對於咱們開發人員來講,寫對象之間互相轉換的代碼是一件極其浪費生命的事情,AutoMapper可以幫助咱們節省很多時間。程序員

一. AutoMapper解決了什麼問題?

要問AutoMapper解決了什麼問題? 難道不是對象映射轉換的問題嗎?
固然是,不過咱們能夠問深刻一些,爲何項目中會出現大量的對象映射轉換?(如下對於非MVC項目也適用)web

在現代的軟件開發中,項目的層級更加的細分,而不一樣層級之間對於對象的需求是有區別的,這就須要在不一樣層級間傳遞數據的時候,必需要轉換數據。app

舉一些實際具體的例子:
在持久層(數據訪問層), 咱們的User對象,多是一個包含User表中全部字段的數據的對象,甚至包含了用戶的Password信息。而在界面層,咱們只是須要顯示用戶的name, email,不須要Password這些額外的信息,同時,它還須要用戶的考勤信息,而這個信息來自於另一張表。
這個例子中,可以發現不一樣層之間,咱們對於數據對象的需求是不一樣的。
每一個層都作了它們職責範圍內的事情:
持久層關注數據,因此只提供數據對象,它無需知道外層如何使用這些數據對象,也沒法知道。
界面層關注數據的呈現,它只關注它要顯示的數據。ide

那麼問題是,誰來彌補它們之間的鴻溝?DTO(Data Transfer Object)——數據傳輸對象。而AutoMapper就是解決其中涉及到的數據對象轉換的工具。工具

clip_image001

在實際開發中,若是你還能夠直接在Business層或者界面層直接使用持久層的對象,由於你認爲這個關係不大,整個項目都是你本身控制的,雖然dirty,可是quick. 做爲一個有些潔癖的程序員,我仍是建議使用DTO在不一樣層級之間傳遞數據。由於當你作更高層級開發的時候,好比開發web service,WCF,Web API這些爲系統外部提供接口的開發時候,你就回明白這些好的習慣和思惟可以幫助你更加好的設計這些外部接口。測試

二. AutoMapper如何使用?

先來看一個簡單的例子,這個例子是定義Order對象到OrderDto對象之間的映射。(咱們把Order稱呼爲源類,OrderDto稱呼爲目標類)ui

Mapper.CreateMap<Order, OrderDto>();//建立映射關係Order –> OrderDto
OrderDto dto = Mapper.Map<OrderDto>(order);//使用Map方法,直接將order對象裝換成OrderDto對象

智能匹配this

AutoMapper可以自動識別和匹配大部分對象屬性:lua

  • 若是源類和目標類的屬性名稱相同,直接匹配
  • 目標類型的CustomerName能夠匹配源類型的Customer.Name
  • 目標類型的Total能夠匹配源類型的GetTotal()方法

自定義匹配規則spa

AutoMapper還支持自定義匹配規則

Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
                        //屬性匹配,匹配源類中WorkEvent.Date到EventDate
                        .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.WorkEvent.Date))
                        .ForMember(dest => dest.SomeValue, opt => opt.Ignore())//忽略目標類中的屬性
                        .ForMember(dest => dest.TotalAmount, opt => opt.MapFrom(src => src.TotalAmount ?? 0))//複雜的匹配
                        .ForMember(dest => dest.OrderDate, opt => opt.UserValue<DateTime>(DateTime.Now));固定值匹配

測試
當定義完規則後,可使用下面的代碼來驗證配置是否正確。不正確拋出異常AutoMapperConfigurationException.

Mapper.AssertConfigurationIsValid();

三. AutoMapper處理多對一映射

咱們開篇提到的問題中,說到界面顯示User的name, email, 還有用戶的考勤信息,而這些信息來自於2張不一樣的表。這就涉及到了多對一映射的問題,2個持久層對象須要映射到一個界面顯示層的對象。

假設咱們的持久層對象是這樣的:

public class User
{
       public int Id { get; set; }
       public string Name { get; set; }
       public string Email { get; set; }
       public string Passworkd { get; set; }
       public DateTime Birthday { get; set; }
}

public class Evaluation
{
       public int Id { get; set; }
       public int Score { get; set; }
}

在Asp.net MVC中,個人界面顯示層的ViewModel是這樣的

public class UserViewModel
{
       public int Id { get; set; }
       public string Name { get; set; }
       public string Email { get; set; }
       public int Score { get; set; }
}

接下來,爲了達到多對一的映射的目的,咱們建立這個EntityMapper類

public static class EntityMapper
{
       public static T Map<T>(params object[] sources) where T : class
       {
           if (!sources.Any())
           {
               return default(T);
           }

           var initialSource = sources[0];
           var mappingResult = Map<T>(initialSource);

           // Now map the remaining source objects
           if (sources.Count() > 1)
           {
               Map(mappingResult, sources.Skip(1).ToArray());
           }
           return mappingResult;
       }

       private static void Map(object destination, params object[] sources)
       {
           if (!sources.Any())
           {
               return;
           }
           var destinationType = destination.GetType();
           foreach (var source in sources)
           {
               var sourceType = source.GetType();
               Mapper.Map(source, destination, sourceType, destinationType);
           }
       }

       private static T Map<T>(object source) where T : class
       {
           var destinationType = typeof(T);
           var sourceType = source.GetType();
           var mappingResult = Mapper.Map(source, sourceType, destinationType);
           return mappingResult as T;
       }
   }

爲了實現多個源對象映射一個目標對象,咱們使用了AutoMapper的方法,從不一樣的源對象逐一匹配一個已經存在的目標對象。下面是實際使用在MVC中的代碼:

public ActionResult Index()
{
          var userId = 23,
          var user = _userRepository.Get(userId);
          var score = _scoreRepository.GetScore(userId);
          var userViewModel = EntityMapper.Map<UserViewModel>(user, score);
          return this.View(userViewModel);
}

四. 使用Profile在Asp.net MVC項目中配置AutoMapper

ProfileAutoMapper中用來分離類型映射定義的,這樣可讓咱們的定義AutoMapper類型匹配的代碼能夠更加分散,合理和易於管理。

利用Profile, 咱們能夠更加優雅的在MVC項目中使用咱們的AutoMapper. 下面是具體的方法:

1.  在不一樣層中定義Profile,只定義本層中的類型映射

繼承AutoMapping的Profile類,重寫ProfileName屬性和Configure()方法。

public class ViewModelMappingProfile: Profile
{
       public override string ProfileName
       {
           get
           {
               return GetType().Name;
           }
       }

       protected override void Configure()
       {
           Mapper.CreateMap......
       }
}

2. 建立AutoMapperConfiguration, 提供靜態方法Configure,一次加載全部層中Profile定義

public class AutoMapperConfiguration
{
       public static void Configure()
       {
           Mapper.Initialize(x => x.AddProfile<ViewModelMappingProfile>());
           Mapper.AssertConfigurationIsValid();
       }
}

3. Global.cs文件中執行

最後,在Global.cs文件中程序啓動前,調用該方法

AutoMapperConfiguration.Configuration() 
相關文章
相關標籤/搜索