惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
Last Week in AI
Last Week in AI
Blog — PlanetScale
Blog — PlanetScale
爱范儿
爱范儿
J
Java Code Geeks
A
About on SuperTechFans
F
Fortinet All Blogs
B
Blog
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Engineering at Meta
Engineering at Meta
Y
Y Combinator Blog
有赞技术团队
有赞技术团队
G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
博客园_首页
博客园 - 叶小钗
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
Docker
云风的 BLOG
云风的 BLOG

博客园 - JustRun

Webpack中publicPath设置 忘记Mysql的root密码怎么办? Visual Studio 2015上安装Entity Framework Power Tools Ubuntu下安装中文输入法 Ubuntu如何选择更新源 Orchard中如何配置远端发布 .Net缓存管理框架CacheManager 全新的membership框架Asp.net Identity(2)——绕不过的Claims 全新的membership框架Asp.net Identity(1)——.Net membership的历史 泛型使用中,解决类型转换问题 .Net中使用SendGrid Web Api发送邮件(附源码) 使用Filter跟踪Asp.net MVC页面加载时间 下一代Asp.net开发规范OWIN(3)—— Middleware 下一代Asp.net开发规范OWIN(2)—— Katana介绍以及使用 下一代Asp.net开发规范OWIN(1)—— OWIN产生的背景以及简单介绍 AngularJs中,如何在render完成之后,执行Js脚本 .Net Collection的一些理解——记录一次向实习生的答疑 代替Reflection(反射)的一些方法 使用PhoneGap开启移动开发之旅
对象映射工具AutoMapper介绍
JustRun · 2014-08-29 · via 博客园 - JustRun

AutoMapper是用来解决对象之间映射转换的类库。对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间。

一. AutoMapper解决了什么问题?

要问AutoMapper解决了什么问题? 难道不是对象映射转换的问题吗?
当然是,不过我们可以问深入一些,为什么项目中会出现大量的对象映射转换?(以下对于非MVC项目也适用)

在现代的软件开发中,项目的层级更加的细分,而不同层级之间对于对象的需求是有区别的,这就需要在不同层级间传递数据的时候,必须要转换数据。

举一些实际具体的例子:
在持久层(数据访问层), 我们的User对象,可能是一个包含User表中所有字段的数据的对象,甚至包含了用户的Password信息。而在界面层,我们只是需要显示用户的name, email,不需要Password这些额外的信息,同时,它还需要用户的考勤信息,而这个信息来自于另外一张表。
这个例子中,能够发现不同层之间,我们对于数据对象的需求是不同的。
每个层都做了它们职责范围内的事情:
持久层关注数据,所以只提供数据对象,它无需知道外层如何使用这些数据对象,也无法知道。
界面层关注数据的呈现,它只关注它要显示的数据。

那么问题是,谁来弥补它们之间的鸿沟?DTO(Data Transfer Object)——数据传输对象。而AutoMapper就是解决其中涉及到的数据对象转换的工具。

clip_image001

在实际开发中,如果你还可以直接在Business层或者界面层直接使用持久层的对象,因为你认为这个关系不大,整个项目都是你自己控制的,虽然dirty,但是quick. 作为一个有些洁癖的程序员,我还是建议使用DTO在不同层级之间传递数据。因为当你做更高层级开发的时候,比如开发web service,WCF,Web API这些为系统外部提供接口的开发时候,你就回明白这些好的习惯和思维能够帮助你更加好的设计这些外部接口。

二. AutoMapper如何使用?

先来看一个简单的例子,这个例子是定义Order对象到OrderDto对象之间的映射。(我们把Order称呼为源类,OrderDto称呼为目标类)

Mapper.CreateMap<Order, OrderDto>();//创建映射关系Order –> OrderDto
OrderDto dto = Mapper.Map<OrderDto>(order);//使用Map方法,直接将order对象装换成OrderDto对象

智能匹配

AutoMapper能够自动识别和匹配大部分对象属性:

  • 如果源类和目标类的属性名称相同,直接匹配
  • 目标类型的CustomerName可以匹配源类型的Customer.Name
  • 目标类型的Total可以匹配源类型的GetTotal()方法

自定义匹配规则

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()