2014-04-04 83 views
3

我需要在平面ViewModel和深層結構化域模型之間映射兩種方法。這將是我們解決方案中的常見情況。AutoMapper:域模型和視圖模型之間的雙向深度映射

我的車型有:

public class Client 
{ 
    ... 
    public NotificationSettings NotificationSettings { get; set; } 
    public ContactDetails ContactDetails { get; set; } 
    ... 
} 

public class NotificationSettings 
{ 
    ... 
    public bool ReceiveActivityEmails { get; set; } 
    public bool ReceiveActivitySms { get; set; } 
    ... 
} 

public class ContactDetails 
{ 
    ... 
    public string Email { get; set } 
    public string MobileNumber { get; set; } 
    ... 
} 

public class ClientNotificationOptionsViewModel 
{ 
    public string Email { get; set } 
    public string MobileNumber { get; set; } 
    public bool ReceiveActivityEmails { get; set; } 
    public bool ReceiveActivitySms { get; set; } 
} 

映射代碼:

Mapper.CreateMap<Client, ClientNotificationOptionsViewModel>() 
    .ForMember(x => x.ReceiveActivityEmails, opt => opt.MapFrom(x => x.NotificationSettings.ReceiveActivityEmails)) 
    .ForMember(x => x.ReceiveActivitySms, opt => opt.MapFrom(x => x.NotificationSettings.ReceiveActivitySms)) 
    .ForMember(x => x.Email, opt => opt.MapFrom(x => x.ContactDetails.Email)) 
    .ForMember(x => x.MobileNumber, opt => opt.MapFrom(x => x.ContactDetails.MobileNumber)); 

// Have to use AfterMap because ForMember(x => x.NotificationSettings.ReceiveActivityEmail) generates "expression must resolve to top-level member" error 
Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>() 
    .IgnoreUnmapped() 
    .AfterMap((from, to) => 
    { 
     to.NotificationSettings.ReceiveActivityEmail = from.ReceiveActivityEmail; 
     to.NotificationSettings.ReceiveActivitySms = from.ReceiveActivitySms; 
     to.ContactDetails.Email = from.Email; 
     to.ContactDetails.MobileNumber = from.MobileNumber; 
    }); 


... 

// Hack as ForAllMembers() returns void instead of fluent API syntax 
public static IMappingExpression<TSource, TDest> IgnoreUnmapped<TSource, TDest>(this IMappingExpression<TSource, TDest> expression) 
{ 
    expression.ForAllMembers(opt => opt.Ignore()); 
    return expression; 
} 

我不喜歡它,因爲:

1)它是繁瑣

2)第二映射幾乎拆除Automapper的功能並手動實現工作 - 它的唯一優勢是i小號參考Automapper整個代碼

任何人都可以提出的一致性:

a)一個更好的方式來使用Automapper深屬性?

b)更好的方式來執行這樣的雙向映射?

c)在這種情況下是否應該打擾使用Automapper的建議?是否有一個令人信服的理由不會恢復到手動編碼的簡單方法?例如:

void MapManually(Client client, ClientNotificationOptionsViewModel viewModel) 
{ 
    viewModel.Email = client.ContactDetails.Email; 
    // etc 
} 

void MapManually(ClientNotificationOptionsViewModel viewModel, Client client) 
{ 
    client.ContactDetails.Email = viewModel.Email; 
    // etc 
} 

-Brendan

附:重構領域模型不是解決方案。

P.P.S通過擴展方法&可以清理上面的代碼來設置深層屬性...但我寧願使用automapper功能。

+0

你能分享你的ViewModel和Domain類的代碼嗎?有理由使用AfterMap而不是ForMember? – thepirat000

+0

這只是示例代碼,但我添加了域/視圖模型類。我使用AfterMap而不是ForMember()來避免「表達式必須解析爲頂級成員」錯誤。 –

+2

AutoMapper從來沒有用於這種情況,可能永遠不會。不要爲此使用它。 –

回答

-3

最後我發現AutoMapper不適合我的場景。

取而代之,我構建了一個定製實用程序,以提供雙向映射&深層屬性映射,允許配置如下。鑑於我們的項目範圍,我相信這是有道理的。

BiMapper.CreateProfile<Client, ClientNotificationsViewModel>() 
    .Map(x => x.NotificationSettings.ReceiveActivityEmail, x => x.ReceiveActivityEmail) 
    .Map(x => x.NotificationSettings.ReceiveActivitySms, x => x.ReceiveActivitySms) 
    .Map(x => x.ContactDetails.Email, x => x.Email) 
    .Map(x => x.ContactDetails.MobileNumber, x => x.MobileNumber); 

BiMapper.PerformMap(client, viewModel); 
BiMapper.PerformMap(viewModel, client); 

道歉我不能分享實施,因爲它是商業工作。不過,我希望它可以幫助其他人知道建立自己的文件並不是不可能的,並且可以提供優於AutoMapper的優點或手動執行。

+1

當然這不是不可能 - AutoMapper做它。這個答案對未來的讀者完全沒用。 –

4

這也可以做到這樣:

Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>() 
    .ForMember(x => x.NotificationSettings, opt => opt.MapFrom(x => new NotificationSettings() { ReceiveActivityEmails = x.ReceiveActivityEmails, ReceiveActivitySms = x.ReceiveActivitySms})) 
    .ForMember(x => x.ContactDetails, opt => opt.MapFrom(x => new ContactDetails() { Email = x.Email, MobileNumber = x.MobileNumber })); 

但並不比你的解決方案非常不同。在新的映射

Mapper.CreateMap<ClientNotificationOptionsViewModel, ContactDetails>(); 

Mapper.CreateMap<ClientNotificationOptionsViewModel, NotificationSettings>(); 

Mapper.CreateMap<ClientNotificationOptionsViewModel, Client>() 
    .ForMember(x => x.NotificationSettings, opt => opt.MapFrom(x => x)) 
    .ForMember(x => x.ContactDetails, opt => opt.MapFrom(x => x)); 

你並不需要指定ForMember因爲屬性有:

此外,您還可以通過創建從你的模型映射到您類做兩個班同名。

+0

感謝您的建議,使用MapFrom(x => x)將有助於減少代碼膨脹。 現在,AutoMapper代碼實際上比手動代碼更加冗長,我正在尋找一個令人信服的理由,不是要簡化它並手動執行,或者自己製作一個簡單的映射工具 - 請參閱編輯註釋 –

+0

因此,您重新創建輪:) – thepirat000

相關問題