用于在域模型之间映射数据的模式

时间:2011-11-28 15:48:59

标签: design-patterns viewmodel models

这是我最近需要做的常见事情,我正在寻找任何常见的模式,以使这更容易。

这一切的主要要点是我有一些数据模型,它们被建模以满足ORM并且纯粹对对象进行CRUD操作。这些模型目前通过存储库/工厂公开(取决于其C或RUD)。

然后我有了一个更具可读性的视图模型,并且有一些UI问题,例如视图之间的验证和映射数据(这是一个ASP.MVC场景,但这种情况可以抽象到大多数情况)。

所以我想说我去了localhost / user / 1,它应该让我在数据库中找到ID为1的用户,然后在UI上显示它。最终,这必须从数据域中提取数据,然后将其映射到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层运行查询,并返回一个视图模型,抽象存储库和从一个模型到另一个模型的复制数据,它只是觉得应该有一个更好的这样做的方法。

3 个答案:

答案 0 :(得分:5)

在.net中你应该看看Automapper

https://github.com/AutoMapper/AutoMapper

它基本上允许你这样做:

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

然后您可以通过说:

在两者之间进行映射
var vmCustomer = Mapper.Map<Domain.Customer, ViewModel.Customer>(domainCustomer);

它可能会为您节省大量的锅炉铭牌代码。

答案 1 :(得分:2)

是。 AutoMapper是适合您的工具。关于这一点的文章很多,但请到源头 - 吉米博加德。他是AutoMapper背后的主人。

请参阅此博客文章http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

AutoMapper现在几天以非常有效的方式支持MVC,你可以使用属性来装饰你的类。应该做什么映射。

/最好的问候Magnus

答案 2 :(得分:1)

在这种特殊情况下,我可能只是在我的表示层中为User编写扩展方法。

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" />
   ...

  

您有一点,但是假设您要为模型添加验证,因此您决定在Name属性中添加[Required]属性。虽然如果你使用共享模型,那么你的数据层需要知道注释,如果你的数据层必须有一些属性你的UI层需要知道它们。在最简单的情况下,你是对的,有时候让一个模型成为事实更容易,只是以不同的方式访问它,但是对于大多数复杂的项目,你会发现自己弄脏双方试图保存几行代码。

我说 - 在这个特殊情况下。这种方法仅适用于映射不值得且只有单向通信(您只需要渲染它)。

当谈到接收帖子时,它会有所不同。大多数“一刀切”的方法都是应用所谓的Thunderdome principle。那就是:

  

所有Controller方法都接受一个ViewModel对象(在某些情况下为零对象)并返回单个ViewModel对象(一个对象进入,一个对象离开)。

但是,我经常更喜欢使用extension / html帮助方式,只需将参数传递给这样的动作:

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

如果参数计数失控(我不打扰,而它是&lt; = 3),我只是封装 他们(我的项目here's an example)。