MVC中如何在视图模型中使用领域模型

11
以下操作是否可行?我知道领域模型不应该在视图中使用,但在视图模型中使用领域模型可以吗?对于一些非常小的模型来说,创建和管理视图模型似乎不值得。
例如:
public class LoginDomainModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string DisplayName { get; set; }
    public long UserTypeID { get; set; }      
    public virtual UserType UserType { get; set; } 
}
public class UserTypeDomainModel
{
    public UserType()
    {
        this.Logins = new List<Login>();
    }
    public long UserTypeID { get; set; }
    public string UserType { get; set; }
    public string Description { get; set; }
    public virtual ICollection<Login> Logins { get; set; }
}

public class LoginViewModel
{
    public string Email { get; set; }
    public long UserTypeID {get; set;}

    //Right here
    public List<UserTypeDomainModel> UserTypesSelectList {get; set;}
}
3个回答

10
我个人在视图中使用领域模型如果它们自然而然地完全吻合。这可能只会发生在CRUD项目(以直接方式编辑领域实体)中,这些项目通常很琐碎。为了保持纯洁性而创建领域实体的完全副本是浪费时间。
永远不会稍微修改领域模型来满足视图的需求。在95%以上的项目中,这就是我发现自己处于的情况。一旦你为了视图而污染领域,就会引入可维护性方面的问题。

直到您的领域实体更新并且视图尚未更新。然后,您会遇到在界面中未呈现的新必填字段而感到头痛。我想这取决于您的领域模型有多频繁更改。 - Brad Christie
@BradChristie:在95%的项目中,我会使用单独的视图模型和领域模型以及一个映射层(automapper很好用)。但对于5%的项目(小而简单的项目...通常是1人项目),这样做就有些过度设计了。 - Eric J.
可能存在安全隐患。我发现在视图/视图模型中使用领域模型实体显著增加了引入“过度发布”攻击形式的安全漏洞的可能性(请参见http://odetocode.com/blogs/scott/archive/2012/03/11/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx)。我还观察到,在视图/视图模型中直接使用领域实体通常是“贫血领域模型”反模式的迹象(http://www.martinfowler.com/bliki/AnemicDomainModel.html)。我将在视图/视图模型中使用“值对象”,而不是实体。 - Nathan
如果您使用类似Automapper的东西而没有像Scott在博客文章中展示的同样谨慎,安全问题仍然存在(排除某些映射)。在一个微不足道的情况下(我认为这是唯一一个没有单独领域模型的情况),无论如何领域模型都将是贫血的,因为根据定义,主题是微不足道的。每当领域有任何丰富性时,出于这个原因,我会为其拥有单独的领域和视图模型。 - Eric J.

1

我曾经长时间地为单独的视图模型和域模型导致的重复感到苦恼。我想强调的是,由于它们的目的不同,这并不是真正的重复,但声明如此多相似的属性仍然感觉“不对”。

在非常小的项目中(特别是具有高度信任的经过身份验证的用户组的项目中),我可能会直接绑定到域模型并完成它。或者,如果视图模型需要不同的结构,则可以混合使用(如@Eric J.所述)。

然而:ModelBinder将尝试将请求中的值与模型上的属性匹配。这意味着您域模型上的任何属性都可能被(流氓)请求填充。有防止此类情况发生的方法,但对我来说,安心胜过花费额外精力创建单独的视图模型。

我认为没有必要为只读、未绑定的值(可能是您的用户类型列表,尽管public virtual ICollection<Login> Logins可能会抵消这一点)创建单独的视图模型。

或者,您可以将领域模型投影到面向UI的抽象层(例如,IEnumerable<SelectListItem>)。您可以使用SelectListItems来处理各种输入机制,因此您不会将自己绑定到特定的UI行为。

即使使用了抽象层,您仍然需要验证请求中不包含非法值。例如,也许只有超级管理员才能分配某些UserTypeDomainModel ID。无论如何抽象,您仍然需要验证这一点。

TLDR:尽可能地将领域模型抽象化,找到适当的抽象层(一个新的视图模型并不总是正确的答案),并对输入验证保持(稍微)谨慎。


1
这取决于你所说的“领域模型”的含义。你是指EF实体吗?还是指业务层对象?
将EF实体传递到视图中通常不是一个好主意,特别是如果你使用默认模型绑定。如果你不小心,这可能会导致安全问题。尽管如果你不小心地将业务对象传递给视图也可能出现相同的问题。
视图模型的一个巨大优势在于,你可以更精细地控制数据映射,因此你可以更轻松地验证只发生正确的映射。
但归根结底,这都取决于你的应用程序。如果它是一个简单的应用程序,那么做更复杂的映射可能不值得。如果它是一个复杂的应用程序,必须长期存在,并且可能会经常更新...那么你肯定应该投入精力。

我指的是EF实体。 - Preston
@Preston - 那就是你的数据模型,而不是你的领域模型。我上面的建议仍然适用。 - Erik Funkenbusch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接