对象映射工具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() 
相关文章
相关标签/搜索