AutoMapper在MVC中的運用01-配置、使用、單元測試、舉例

Darren Ji發表於2014-02-26

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的各種使用場景。


相關文章