ASP.NET MVC体系结构:通过组合,继承或重复来实现ViewModel?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (62)

我使用ASP.NET MVC 3和实体框架4.1代码优先。

假设我有一个User实体:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }        
}

在我编辑它时我UserController想添加一个PasswordConfirmation字段并验证PasswordConfirmation == Password

1.通过组合

我的第一次尝试是:

public class EditUserModel
{
    [Required]
    public User User { get; set; }

    [Compare("User.Password", ErrorMessage = "Passwords don't match.")]
    public string PasswordConfirmation { get; set; }
}

在这种情况下,客户端验证 (客户端验证工作是巧合。)不起作用,并且服务器端验证失败,并显示以下消息:无法找到名为User.Password的属性

我认为最好的解决方案,在这种情况下,将创建一个自定义 CompareAttribute

实施 IValidatableObject

public class EditUserModel : IValidatableObject
{
    [Required]
    public User User { get; set; }
    public string PasswordConfirmation { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(this.PasswordConfirmation != this.User.Password)
            return new[] { new ValidationResult("Passwords don't match", new[] { "PasswordConfirmation " }) };

        return new ValidationResult[0];
    }
}

在这种情况下,服务器端验证有效,客户端验证不再起作用。实现IClientValidatable看起来有点太复杂,我更喜欢在这种情况下没有客户端验证。

2.通过继承

public class EditUserModel : User
{
    [Compare("Password", ErrorMessage = "Passwords don't match.")]
    public string PasswordConfirmation  { get; set; }
}

当试图直接保存EditUserModel使用EF它不工作,我得到一些一些错误信息有关的EditUserModel元数据,以便我使用AutoMapper要转换UserEditUserModel和倒退。此解决方案的工作原理,但它更复杂,因为我必须从模型转换到视图模型和向后。

3.通过重复

(由Malte Clasen推荐)

视图模型将具有模型的所有属性和附加属性。AutoMapper可以用来从一个转换到另一个。

public class EditUserModel {    
  public string Name { get; set; }    
  public string Email { get; set; }    
  public string Password { get; set; }   
  [Compare("Password", ErrorMessage = "Passwords don't match.")]     
  public string ConfirmPassword { get; set; }        
}

这是我最不喜欢的解决方案,因为代码重复(DRY)

问题

在这种情况下继承,组成和重复有什么优点和缺点?

有没有一种简单的方法来同时进行客户端和服务器端验证,而无需将模型转换为视图模型和向后?

提问于
用户回答回答于

在之前一直与这个问题斗争的时候,我已经在三种情况下都走了。总的来说,我在MVC项目中看到的大多数意见都支持复制,并且专门为每个视图构建了ViewModel。以这种方式,你会使用的约定是UserDetailsViewModelUserCreateViewModel。正如你所说的那样,那时AutoMapper或其他一些自动映射工具将被用来从你的域对象转换到这些平面ViewModels。

虽然我也不喜欢重复代码,但我也不喜欢使用验证或其他视图特定的属性来污染我的域对象。另一个好处,尽管几乎没有人会与之抗衡(无论专业人士说什么),但是可以通过某些方式操纵域对象,而无需操纵ViewModel。我提到这是因为它通常被引用,并不是因为它对我很重要。

最后,使用真正平坦的ViewModel可以实现更清晰的标记。当我使用了构图时,我经常会用创建类似名称的HTML元素来创建错误User.Address.Street。一个平面ViewModel至少减少了我做这件事的可能性(我知道,我总是可以使用HtmlHelper例程来创建元素,但这并不总是可行)。

无论如何,我最近的项目也需要单独的ViewModel。他们都是基于NHibernate的,并且在NHibernate对象上使用代理使得不可能直接将它们用于视图。

这是我以前提到的一篇很好的文章:http : //geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

用户回答回答于

例如,在这种情况下,也可以考虑用于域和视图模型的独立类

public class EditUserModel {    
  public string Name { get; set; }    
  public string Email { get; set; }    
  public string Password { get; set; }        
  public string ConfirmPassword { get; set; }        
}

如果Id存储在URL中。如果你想避免User和EditorUserModel实例之间的手动拷贝,AutoMapper可以为你提供帮助。这样,可以轻松地将视图模型中的密码字符串与域模型中的密码哈希解耦合。

扫码关注云+社区