ASP.NET MVC体系结构:ViewModel是由构成,inheritance还是重复?

我使用ASP.NET MVC 3和entity framework4.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字段并validationPasswordConfirmation == Password

1.通过构图

我的第一个尝试是:

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

在这种情况下,客户端validation 工作但是 编辑:客户端validation工作是巧合。) 不起作用服务器端validation失败 ,出现以下消息: 无法find名为User.Password属性

编辑:我认为最好的解决scheme,在这种情况下,将创build一个自定义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]; } } 

在这种情况下, 服务器端validation工作,客户端validation不再工作 。 实现IClientValidatable似乎有点太复杂,我更喜欢在这种情况下没有客户端validation。

2.通过inheritance

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

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

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; } } 

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

问题

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

有没有简单的方法来同时进行客户端和服务器端validation,而不必将模型转换为视图模型和向后?

以前我一直在努力解决这个问题,在这三个问题上我都有过不同的情况。 一般来说,大多数的观点都赞成MVC项目中的重复,而ViewModel专门为每个视图构build。 以这种方式,您将使用的惯例是像UserDetailsViewModelUserCreateViewModel 。 正如你所说的那样,在这一点上AutoMapper或其他自动映射工具将被用来从你的域对象转换到这些平面ViewModels。

虽然我也不喜欢重复代码,但我也不喜欢用validation或其他视图特定的属性来污染我的域对象。 另外一个好处,虽然几乎没有人能够应付(不pipe所有的专业人士都说了什么),但是你可以通过某些方式来操作你的域对象,而不必操纵你的ViewModel。 我提到,因为它通常被引用,而不是因为它对我很重。

最后,使用一个真正的平面ViewModel使清洁标记。 当我使用构图时,我经常犯的错误创build的HTML元素的名称就像User.Address.Street 。 一个平面ViewModel至less减less了我做这件事的可能性(我知道,我总是可以使用HtmlHelper例程来创build元素,但这并不总是可行的)。

无论如何,我最近的项目也需要单独的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可以帮助你。 这样,您可以轻松地将视图模型中的密码string从域模型中的密码哈希中解耦。

我试图解决这个问题,并且find了一个不涉及重复代码的解决scheme。 这是一种解决方法,但在我看来,它比其他解决scheme更好。

您拥有所有validation的用户模型:

 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 } } 

这样validation按预期工作。

希望这可以帮助。

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

为什么不使用应用于实体的元数据类? 使用LINQ-SQL,将分配的元数据同时考虑到客户端以及服务器端validation。

从我所了解的[MetaDataType]属性的应用类似于inheritance,只有它的工作没有实现一个新的类(模型)改变基本的实体。

另外,你可能想要尝试的另一个select是创build一个自定义属性 – 我曾经为了一个类似的目的做了一次。 本质上是一个标志,表明一个成员的坚持。

所以我会有一个实体定义如下:

 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;} } 

此外,我不知道你在做什么来存储数据,但我已经挂钩到我的DataContext的OnInserting,OnEditing,OnDeleting函数,基本上删除了任何具有我的自定义属性的成员。

我喜欢这个方法,因为我们为每个模型使用了很多临时的,相当algorithm的数据(构build商业智能的良好用户界面),这些数据没有保存在数据库中,而是在模型函数,控制器等内部的任何地方使用 – 所以我们使用依赖在所有模型库和控制器中进行注入,所以我们为每个表使用所有这些额外的数据点。

希望有所帮助!

PS: – 组合vsinheritance – 它确实取决于应用程序的目标用户。 如果是针对安全问题较less的Intranet应用程序,并且用户/浏览器环境受到控制,则只需使用客户端validation即组合。

我赞成组合而不是inheritance。

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

您应该只存储一个盐EditUserModel散列,而您的EditUserModel应该有两个string属性,用于密码和密码确认,这不是您的表中的字段。