2011-11-28 89 views
3

這是我最近一直需要做的常見事情,我一直在尋找任何常見模式,以使其更容易一些。用於在域模型之間映射數據的模式

它的主要要點是我有一些數據模型,它們被建模爲滿足ORM,並且純粹對對象執行CRUD操作。這些模型目前通過存儲庫/工廠暴露(取決於其C或RUD)。

然後我有一個視圖模型,這是一個更具可讀性,並與UI關注,如視圖之間的驗證和映射數據(這是一個ASP.MVC場景,但這種情況可以抽象爲大多數的情況下)。

因此可以說我去localhost/user/1,這應該去,並讓我的用戶在數據庫中的Id 1,然後顯示在用戶界面上。最終,這必須從數據域中拉下數據,然後將其映射到UI模型以進行顯示。

下面是一個示例場景:

public class OrmUser 
{ 
    public int Id {get;set;} 
    public string Name {get;set;} 
    public IList<Permission> Permissions {get;set;} 
} 

public class UiUser 
{ 
    [Required] 
    public int Id {get;set;} 
    [Required] 
    public string Name {get;set;} 
    public bool IsUserAdmin {get;set;} 
} 

public class UserMapper : IMapper<UiUser> 
{ 
    public UiUser Get(int id) 
    { 
     var ormUser = UserRepository.Get(id); 
     var uiUser = new UiUser 
     { 
      Id = ormUser.Id, 
      Name = ormUser.Name, 
      IsUserAdmin = IsUserAdmin(ormUser.Permissions) 
     } 
    } 

    private bool IsUserAdmin(IList<Permission> permissions) 
    { 
     return permissions.SomeLinq(ToFindIfTheyAreAnAdmin); 
    } 
} 

這是一個簡單的例子,但顯示了數據模型,如何含有大量相同類型的信息,但這個觀點有問題,你不關心所有的信息只是它的一個子集。通過這種方式,您不但可以抽象映射,還可以抽象與數據域的通信,但是您需要爲每種類型編寫一個映射器類,並且上面假定它是單向映射,而不是雙向映射將需要更多的代碼。

那麼你們如何進行這種映射?目前我正在編寫基本上允許UI層運行查詢的抽象映射器,並獲取視圖模型,將存儲庫和複製數據從一個模型抽象到另一個模型,並且它只是覺得應該有一個更好的這樣做的方法。

+0

至於我的猜測是非常抽象的,你究竟想要映射到什麼? – sll

+0

那麼它不是一個具體的模型,它的意思是有點抽象。我相信現在大多數Web應用程序都有多個層/域,它們包含相似的模型以表示相同類型的數據。我只是在尋找一種好的模式來管理層與層之間的映射和通信。雖然Paul已經給出了一個很好的答案,但會在我的原始問題中添加一些虛擬模型,以幫助查看後面的內容。 – Grofit

+0

另外值得注意的是,雖然這是一個.net示例,但真正可以在任何語言中出現相同的情況。因此,任何語言不可知論模式都將是理想的,儘管用於緩解給定平臺的這個任務的框架也不錯...... – Grofit

回答

5

在.NET你應該看看Automapper

https://github.com/AutoMapper/AutoMapper

它本質上允許你做:

CreateMap<Domain.Customer, ViewModel.Customer>() 

,您可以在兩者之間說再圖:

var vmCustomer = Mapper.Map<Domain.Customer, ViewModel.Customer>(domainCustomer); 

它可能會爲您節省大量的鍋爐板碼。

2

是的。 AutoMapper是您的工具。有很多關於這個的文章,但去源 - 吉米Bogard。他是AutoMapper背後的高手。

看看這個博客文章http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

AutoMapper現在天MVC支持在一個非常有效的方式,您可以與上應該做些什麼映射屬性裝飾你的類。

/最好的問候馬格努斯

+0

向雙方添加屬性會讓事情變得更加骯髒,我正試圖爲雙方提供一個乾淨的抽象。儘管你對.net正確,但它似乎是自動映射器的選擇。希望更多的語言不可知的解決方案,但我認爲AutoMapper和ValueInjector等背後的原理可以適用於其他語言/平臺。 – Grofit

+1

我沒有使用Jimmys的最新插件到MVC,所以你可以使用Attributes。是的,它會變得有點骯髒。它也取決於您的域模型與您的視圖模型的相似程度。如果您需要大量幫助AutoMapper,最好跳過屬性並像普通一樣使用AutoMapper(映射配置文件)。但是映射是我的交叉問題,所以我將映射移出一個單獨的項目並將其包裝到Facade(IAssamble)中。然後,您可以將IAssemble服務接口注入到您想要的位置。我喜歡替換AutoMapper - 做到這一點。但是IAssemble界面仍然可以完好無損。 –

1

在這種特殊情況下,我可能只是寫在我的表現層爲用戶的擴展方法。

public static class UserPresentationExtensions{ 
public bool IsAdmin(this User user){ 
    return permissions.SomeLinq(ToFindIfTheyAreAnAdmin); 
} 
} 

查看會是這個樣子:

@model User 
<fieldset> 
<legend>User: @Model.Name</legend> 
@if(user.IsAdmin()){ 
    User is an admin 
} 
</fieldset> 

爲了避免重複導入的命名空間,將使用web.config文件爲:

<configuration> 
<system.web.webPages.razor> 
    <pages pageBaseType="System.Web.Mvc.WebViewPage"> 
    <namespaces> 
    <add namespace="MyProject.PresentationExtensions" /> 
    ... 

你有一個點,但是可以說你想爲你的模型添加驗證,所以你決定添加一個[Required]屬性到你的Name屬性。儘管如此,如果您使用共享模型,那麼您的數據層需要知道註釋,並且如果您的數據層必須具有UI層需要知道的一些屬性。在最簡單的情況下,你是對的,有時只有1個模型是真實的,只是以不同的方式訪問它,但對於大多數複雜的項目,你會發現自己弄髒雙方試圖節省幾行的代碼。

我說 - 在這個特殊情況下。這種方法只適用於映射不是真的值得,而且只有單向通信(你只需要渲染它)。

說到接收帖子,它有點不一樣。大多數「一刀切」的方法將應用所謂的Thunderdome principle。那就是:

所有控制器方法在一個ViewModel對象(或零對象在某些情況下)並返回一個ViewModel對象(一個對象進入,一個對象離開)。

然而,很多時候我喜歡去擴展/ HTML輔助方法,只是根據參數傳遞給操作是這樣的:

public void BatheCat(int id /* cat id */, int bathId, string shampoo){ 
... 
} 

如果參數個數失控(我不打擾,而它的< = 3),我只是封裝了 他們(我的項目中的here's an example)。

+0

這樣你就需要爲幾乎所有你想要映射的模型或字段編寫擴展方法或者某種類型的自定義邏輯。它*雖然是一個有效的解決方案,但我不認爲當你在不同的領域中有10個以上的模型時,它會導致一個可管理的代碼庫。 – Grofit

+0

@Grofit有時候這比映射看起來與域模型相同的視圖模型簡單得多。這是我的觀點。 –

+0

你有一個觀點,但是可以說你想向你的模型添加驗證,所以你決定添加一個[Required]屬性到你的Name屬性。儘管如此,如果您使用共享模型,那麼您的數據層需要知道註釋,並且如果您的數據層必須具有UI層需要知道的一些屬性。在最簡單的情況下,你是對的,有時只有1個模型是真實的,只是以不同的方式訪問它,但對於大多數複雜的項目,你會發現自己弄髒雙方試圖節省幾行的代碼。 – Grofit

相關問題