ASP.. NET MVC架构:通过组合、继承或复制的视图模型

本文关键字:继承 复制 模型 视图 组合 MVC NET 架构 ASP | 更新日期: 2023-09-27 18:04:48

我正在使用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的属性。密码

编辑:我认为最好的解决方案,在这种情况下,将是创建自定义的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; }
}

当尝试使用EF直接保存EditUserModel不起作用时,我得到一些关于EditUserModel元数据的错误消息,所以我使用AutoMapperUser转换为EditUserModel和向后。这个解决方案工作,但它更复杂,因为我必须从模型转换到视图模型和向后。

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)

在这种情况下继承、组合和复制的优缺点是什么?

是否有一种简单的方法可以同时拥有客户端和服务器端验证,而无需将模型转换为视图模型和向后?

ASP.. NET MVC架构:通过组合、继承或复制的视图模型

之前曾与这个问题作过斗争,我曾在各种情况下与所有三个。一般来说,我看到的大多数观点都赞成在MVC项目中进行复制,为每个视图专门构造一个ViewModel。在这种方式下,您将使用类似UserDetailsViewModelUserCreateViewModel的约定。正如你所说,在这一点上,AutoMapper或其他一些自动映射工具将被用来从你的领域对象转换到这些平面视图模型。

虽然我也不喜欢重复代码,但我也不喜欢用验证或其他特定于视图的属性污染我的域对象。另一个优点是,您可以以某种方式操作域对象,而不必操作viewmodel,尽管无可否认,几乎没有人需要与之斗争(不管所有的专业人士怎么说)。我之所以提到这一点,是因为它经常被引用,而不是因为它对我来说很重要。

最后,使用真正平面的ViewModel可以使标记更清晰。当我使用组合时,我经常在创建名称类似User.Address.Street的HTML元素时犯错误。一个扁平的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可以帮助您。通过这种方式,您可以轻松地将视图模型中的密码字符串与域模型中的密码散列解耦。

我试图解决这个问题,我找到了一个不涉及复制代码的解决方案。这是一种变通方法,但在我看来,它比其他提出的解决方案要好。

你有用户模型与所有的验证:

public class UserModel
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }        
}

用新模型组合以前的模型

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

技巧是在动作中,您可以收到多个模型:

[HtttPost]
public ActionResult UpdateInformation(UserModel user, EditUserModel editUserModel) {
    if (ModelState.IsValid) {
         // copy the inner model to the outer model, workaround here:
         editUserModel.User = user
         // do whatever you want with editUserModel, it has all the needed information
    }
}

以这种方式验证按预期工作。

我不太使用实体模型,我更喜欢LINQ - SQL模型,所以这可能是不正确的:

为什么不使用应用于实体的元数据类呢?对于LINQ - SQL,分配的元数据会被客户端和服务器端验证考虑在内。

根据我的理解,[MetaDataType]属性的应用程序类似于继承,只是它不需要实现一个新的类(模型)来改变基本实体。

另外,您可能想尝试的另一个选项是创建自定义属性——我曾经出于类似的目的这样做过一次。本质上是一个标志,表示成员的持久性。

那么我将有一个定义如下的实体:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }     
    [DoNotPersist]   
    public string ConfirmPassword {get; set;}
}

另外,我不知道你在做什么来存储数据,但我已经钩了一个覆盖到oninsert, OnEditing, OnDeleting函数为我的DataContext基本上删除了任何成员有我的自定义属性。

我喜欢这种简单的方法,因为我们使用了很多临时的,而不是算法的数据,每个模型(为商业智能构建良好的UI),这些数据不是保存在数据库中,而是在模型函数,控制器等内部的任何地方使用-所以我们在所有模型存储库和控制器中使用依赖注入,所以我们有所有这些额外的数据点每个表玩。

希望有帮助!

PS:-组合vs继承-这真的取决于应用程序的目标用户。如果是内部网应用程序,安全性不是问题,并且用户/浏览器环境受到控制,那么只需使用客户端验证,即:组合。

我更喜欢组合而不是继承。

在你的用户密码的情况下,它看起来像你实际上存储的密码在Users表明文,这是非常,非常糟糕。

您应该只存储一个加盐散列,并且您的EditUserModel应该有两个字符串属性用于密码和密码确认,它们不是表中的字段。