AutoMapper在MVC中的运用01-配置、使用、单元测试、举例

MVC中,如果想在Domain Model和View Model之间建立映射,用AutoMapper是一个不错的选择。不仅如此,AutoMapper能在不同对象之间建立映射,比如string与int类型, DateTime与int类型,接口与实现类,等等。本篇主要总结AutoMapper在MVC中的配置、使用、单元测试,以及各种映射场景。


注意:

如果通过NuGet下载最新版的AutoMapper,需要注意的是:有些方法,比如ForMember方法,和以前不一样。还有一些方法已经过期。


  配置


□ 全局配置

 1 public class MvcApplication : System.Web.HttpApplication
 2 
 3 {
 4 
 5     protected void Application_Start()
 6 
 7     {
 8 
 9         ...
10 
11         ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
12 
13         CreateMaps();
14 
15     }
16 
17 
18     public void CreateMaps()
19 
20     {
21 
22         AutoMapper.Mapper.Reset();
23 
24         AutoMapper.Mapper.CreateMap<CustomerCreateEditViewModel, Customer>();
25 
26         AutoMapper.Mapper.AssertConfigurationIsValid();
27 
28     }
29 
30 }
View Code

这种方法不太利于单元测试。


□ 全局配置 + 静态类配置


AutoMapper静态配置类:


 1     public static class AutoMapperWebConfiguration
 2 
 3     {
 4 
 5         public static void Configure()
 6 
 7         {
 8 
 9             Mapper.Initialize(cfg =>
10 
11             {
12 
13                 cfg.AddProfile(new UserProfile());
14 
15             });
16 
17         }
18 
19     }
View Code

UserProfile继承于AutoMapper的Profile类。通过这种继承,我们可以创建不同的映射规则。比如一套规则用于Domain Model转换成View Model,一套规则用于View Model转换成Domain Model。

 1     public class UserProfile : Profile
 2 
 3     {
 4 
 5         protected override void Configure()
 6 
 7         {
 8 
 9             AddFormatter<MoneyFormatter>();           
10 
11             Mapper.CreateMap<Order, OrderListViewModel>();
12 
13         }
14 
15     }
View Code

最后在全局注册。


AutoMapperWebConfiguration.Configure();


  单元测试


当项目中有比较多的model的时候,通过单元测试,可以发现映射中存在的问题,而不是等到程序运行的时候。


 1     [TestClass]
 2 
 3     public class AutoMapperConfigurationTester
 4 
 5     {
 6 
 7         [TestMethod]
 8 
 9         public void TestMethod1()
10 
11         {
12 
13             AutoMapperWebConfiguration.Configure();
14 
15             Mapper.AssertConfigurationIsValid();
16 
17         }
18 
19     }
View Code

  简单例子


□ Domain Models


 1 public class Customer
 2 
 3 {
 4 
 5 public string FirstName{get;set;}
 6 
 7 public string LastName{get;set;}
 8 
 9 public string Email{get;set;}
10 
11 pubic Address HomeAddress{get;set;}
12 
13 public string GetFullName()
14 
15 {
16 
17 return string.Format("{0}{1}", FirstName, LastName);
18 
19 }
20 
21 }
22 
23 
24 public class Address
25 
26 {
27 
28 public string Address1{get;set;}
29 
30 public string Address2{get;set;}
31 
32 public string City{get;set;}
33 
34 public string PostalCode{get;set;}
35 
36 public string Country{get;set;}
37 
38 }
View Code

□ View Model


 1 public class CustomerListViewModel
 2 
 3 {
 4 
 5 public string FullName{get;set;}
 6 
 7 public string Email{get;set;}
 8 
 9 public string HomeAddressCountry{get;set;}
10 
11 }
View Code

□ Controller


 1 public class CustomersController : Controller
 2 
 3 {
 4 
 5 private readonly ICustomerService m_CustomerService;
 6 
 7 public CustomersController(ICustomerService customerService)
 8 
 9 {
10 
11 m_CustomerService = customerService;
12 
13 }
14 
15 
16 public ActionResult Index()
17 
18 {
19 
20 IList<Customer> customers = m_CustomerService.GetCustomers();
21 
22 //为了演示方便,映射规则没有写在统一的静态类中
23 
24 Mapper.CreateMap<Customer, CustomerListViewModel>();
25 
26 IList<CustomerListViewModel> viewModelList = Mapper.Map<IList<Customer>, IList<CustomerListViewModel>>(customers);
27 
28 return View(viewModelList);
29 
30 }
31 
32 }
View Code

□ 要点


AutoMapper的"神奇"是建立在惯例和配置之上的。

○ 目标和源的属性名要尽可能保持一致。

○ 当源的属性是复杂类型时,目标属性如果遵循"源属性+源属性所对应类中的某个字段"的惯例,就像这里的HomeAddressCountry,就能拿到源中复杂类型属性所对应类中的字段。

○ 源中的"Get+其它"形成的方法,在目标中只要把"其它"作为属性名,就可以拿到源中方法的返回值,就像源中的GetFullName()方法,对应目标中的FullName属性。

○ 创建映射永远是类与类间的映射,而通过源获取目标,这里的源可以是单个类,也可以是集合,就像 Mapper.Map<IList<Customer>, IList<CustomerListViewModel>>(customers)。


□ 出处


以上参考了这篇博文:http://bengtbe.com/blog/2009/04/14/using-automapper-to-map-view-models-in-asp-net-mvc/



  把Domain Model与View Model的映射放到系统属性里实现


有时,为了代码更大程度的简洁,我们可以把系统属性里,以Aspect Oriented Programming(AOP),面向切面编程的思想来实现。

通过ActionFilterAttribute过滤系统属性可以控制发生在Action方法之前和之后的事件。

如果Domain Model转换成View Model,那我们就让自定义事件发生在Action方法之后。

如果View Model转换成Domain Model,那我们就让自定义事件发生在Action方法之前。


□ Domain Model转换成View Model


 1     public class DomainToViewAttribute : ActionFilterAttribute
 2 
 3     {
 4 
 5         private readonly Type _destType;
 6 
 7         private readonly Type _sourceType;
 8 
 9 
10         public DomainToViewAttribute(Type sourceType, Type desType)
11 
12         {
13 
14             _sourceType = sourceType;
15 
16             _destType = desType;
17 
18         }
19 
20 
21         public override void OnActionExecuted(ActionExecutedContext filterContext)
22 
23         {
24 
25             var domainModel = filterContext.Controller.ViewData.Model;
26 
27             var viewModel = Mapper.Map(domainModel, _sourceType, _destType);
28 
29             filterContext.Controller.ViewData.Model = viewModel;
30 
31         }
32 
33     }
View Code

□ View Model转换成Domain Model


 1     public class ViewToDomainAttribute : ActionFilterAttribute
 2 
 3     {
 4 
 5         private readonly Type _desType;
 6 
 7         private readonly Type _sourseType;
 8 
 9 
10         public ViewToDomainAttribute(Type sourceType, Type desType)
11 
12         {
13 
14             _sourseType = sourceType;
15 
16             _desType = desType;
17 
18         }
19 
20 
21         public override void OnActionExecuting(ActionExecutingContext filterContext)
22 
23         {
24 
25             var viewModel = filterContext.Controller.ViewData.Model;
26 
27             var domainModel = Mapper.Map(viewModel, _sourseType, _desType);
28 
29             filterContext.Controller.ViewData.Model = domainModel;
30 
31         }
32 
33     }
View Code

□ 把自定义系统属性打到Action方法之上


    [DomainToView(typeof(IEnumerable<Customer>), typeof(IEnumerable<CustomerInfo>))]

    public ViewResult Index()    



接下来的几篇将介绍AutoMapper的各种使用场景。


原文地址:https://www.cnblogs.com/darrenji/p/3570474.html