2014年3月4日 星期二

AutoMapper - Complex Type 使用 Custom value resolvers 設定屬性轉換

在上一篇文章「AutoMapper - Complex Type 的資料對映」裡說明了幾種對於 Complex Type 的資料對映方式,其實還有一種方式,AutoMapper 提供了一種「Custom value resolvers(自定義值解析器)」,讓我們可以另外定義資料解析以及轉換的方式,以彈性的方式進行目標類別物件的資料對映。

這篇文章將會以前篇文章的程式來做示範。

 


下面是前一篇文章裡的程式,其中針對 ComplexModel 類別裡的 OrderModel 屬性有做資料對映的設定,

image

我必須說這程式的作法並不是很好,雖然我們在取得 Customer 資料時有特別過濾了是否有訂單資料的條件,但是這樣的做法式很有針對性的,是因為為了要讓程式看起來合理且不會發生錯誤,另外在設定資料對映的地方也是一樣,因為已經知道在取得資料時就已經會做一層過濾,所以不怕會有例外(Exception)的發生,所以就直接去取出第一筆訂單資料的值來做對映。

另外就是在做資料對映設定的部份,將全部的資料對映做一次性的設定,這樣的作法看起來好像結構比較完整,但是遇到比較特殊的對映設定時就會變得複雜,而且如果同樣的資料對映設定在其他的 Complex Type 也有的話,那是否就表示在其他地方又要重複的出現一樣的程式呢?

 

AutoMapper Wiki  - Custom value resolvers

在上面說明「Custom value resolvers」的連結裡可以看到,我們可以建立自定義 ValueResolver 類別,然後在 ResolveCore 方法裡進行資料對映的處理,而原本的 Mapper.CreateMap 設定裡只需使用 ResolveUsing 指定的 ValueResolver 類別與要進行對映轉換的原始資料就可以,而且建立的 ValueResolver 類別也可以在其他的資料對映設定重複使用。

首先建立 Customer ValueResolver 類別,要繼承「ValueResolver<TSource, TDestination>」類別,並且覆寫 ResolveCore 方法,在 ResolveCore 方法裡做資料對映的處理,

image

而原本 Mapper.CreateMap 設定裡,對於目標類別 ComplexModel 的 OrderModel 屬性的對映設定就是使用 ResolveUsing 並且指定使用 OrderModelResolver 類別,最後記得指定要進行資料對映的原始資料,

image

執行結果

image

 

接著我們可以將資料對映的設定從 Controller 裡抽離出來,使用之前文章「AutoMapper 的設定 (Configuration)」的內容,將設定放到 AutoMapperConfig.cs 內容,讓 Controller 與資料對映設定的程式不要混雜在一起而有所區隔。

AutoMapperConfig.cs

public class AutoMapperConfig
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<ComplexModelProfile>();
        });
    }
 
    public class ComplexModelProfile : Profile
    {
        public override string ProfileName
        {
            get
            {
                return "ComplexModelProfile";
            }
        }
 
        protected override void Configure()
        {
            Mapper.CreateMap<Customer, ComplexModel>()
                  .ForMember(d => d.CustomerID, o => o.MapFrom(s => s.CustomerID))
                  .ForMember(d => d.Customer, o => o.MapFrom(s => s))
                  .ForMember(
                      d => d.OrderModel,
                      o => o.ResolveUsing<OrderModelResolver>()
                            .FromMember(s => s.Orders));
        }
 
        public class OrderModelResolver
            : ValueResolver<ICollection<Order>, OrderModel>
        {
            protected override OrderModel ResolveCore(ICollection<Order> source)
            {
                if (!source.Any())
                {
                    return null;
                }
                var order = source.OrderBy(x => x.OrderID).FirstOrDefault();
                var dest = new OrderModel()
                {
                    OrderID = order.OrderID,
                    OrderDate = order.OrderDate
                };
                return dest;
            }
        }
    }
}

記得要在 Global.asax 的 Application_Start() 方法裡加入 AutoMapperConfig.Configure();

image

最後原本 Contoller 裡的程式因為將資料對映設定的內容放到 AutoMapperConfig.cs 內,所以程式就只會剩下取得資料以及資料對映的部份,最後的執行結果仍然一樣,

image

可以跟之前的程式做個比對,

image

 

善用 Custom Value Resolver 讓複雜的資料對映設定另外處理,此外也可以在不同的資料對映設定裡重複使用,並且將程式流程內的程式抽離出來,讓各自的程式有所區隔以及責任區分,如此一來程式就會比較好維護。

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。