ASP.NET MVC Architecture: ViewModel by Composition, Inheritance, or Duplication?

I am using ASP.NET MVC 3 and Entity Framework 4.1 first.

Say I have a User object:

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

When editing in my UserController I want to add the PasswordConfirmation field and make sure PasswordConfirmation == Password

1. The composition

My first attempt:

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

In this case, client-side validation works but ( Edit: client-side validation work was a coincidence.) Does not work and server-side validation failed with the following message: Could not find a property named User.Password

Edit: I think the best solution in this case would be to create a custom CompareAttribute

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

In this case, server-side validation works , but client-side validation no longer works . Implementing IClientValidatable seems too complicated, and I prefer not to check on the client side in this case.

2. By inheritance

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

When I try to directly save EditUserModel using EF, this will not work, I get some error message in the EditUserModel metadata, so I use AutoMapper to convert from User to EditUserModel and back. This solution works , but it is more complicated because I need to convert from model to view model and back.

3. By duplication

(proposed by Malte Clasen)

The view model will have all the model properties plus additional ones. AutoMapper can be used to convert from one to another.

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

This is the solution I least like because of code duplication (DRY)

Questions

What are the advantages and disadvantages of inheritance, compilation and duplication in this case?

Is there an easy way to check both on the client side and on the server side without converting the model into a view model and back?

+51
c # architecture asp.net-mvc
Aug 05 '11 at 9:20 a.m.
source share
5 answers

Struggling with this issue before, on various occasions I left with all three. In general, most of the opinions I've seen prefer duplication in an MVC project, with a ViewModel created specifically for each view. So the convention you use is similar to UserDetailsViewModel and UserCreateViewModel . As you said, at this point AutoMapper or some other automatic matching tool will be used to convert from your domain objects to these flat ViewModels.

While I also don’t like repeating the code, I also don’t like polluting my domain objects with validation or other viewing-specific attributes. Another advantage, although admittedly almost no one had to fight (no matter what all the professionals say), is that you can somehow manipulate the objects of your domain without necessarily manipulating your ViewModels. I mention this because it is often quoted not because it carries a lot of weight for me.

Finally, using a truly flat ViewModel makes cleaner markup. When I used composition, I often made mistakes by creating HTML elements with names that are similar to User.Address.Street . A flat ViewModel reduces at least my likelihood of doing this (I know, I could always use HtmlHelper routines to create elements, but this is not always possible).

These days, my recent projects also pretty much needed separate ViewModels. They were all based on NHibernate, and the use of proxies in NHibernate objects prevents them from being used directly for representations.

Refresh is the good article I mentioned in the past: http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

+24
Aug 05 '11 at 12:18
source share

You can also consider independent classes for domain models and representations, in this case for example

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

if the id is stored in the url. If you want to avoid a manual copy between instances of User and EditorUserModel, AutoMapper can help you. Thus, you can easily separate the password string in your view model from the password hash in your domain model.

+6
Aug 05 2018-11-11T00:
source share

I am trying to figure this out and I have found a solution that does not include code duplication. This is a kind of workaround, but in my opinion, it is better than the other solutions offered.

You have a user model with all the verification:

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

You create the previous model with the new model.

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

Focus in action, you can get more than one model:

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

Thus, the check works as expected.

Hope this helps.

+2
Oct 11 '12 at 15:07
source share

I don’t use Entity Models too much, I prefer LINQ to SQL models, so this may not be true:

Why not use the metadata class that applies to the object? With LINQ-SQL, the assigned metadata is taken into account both for the client side and for server-side validation.

From what I understand, the application of the [MetaDataType] attribute is similar to inheritance, it works without implementing a new class (model) for changing the main entity.

Also, another option that you might want to try is to create a custom attribute — I did this once for a similar purpose. Essentially, this is a flag indicating the constancy of a member.

So, I will have an entity defined as follows:

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

Also, I don’t know what you are doing to store the data, but I caught the override in the OnInserting, OnEditing, OnDeleting functions for my DataContext, which basically removed all members having their own attribute.

I like this method simply because we use a lot of temporary, rather algorithmic data for each model (creating a good user interface for Business Intelligence), which is not stored in the database, but used everywhere inside the model functions, controllers, etc. we use dependency injection in all repositories and model controllers, so we have all these extra data points for every table that we can play.

Hope this helps!

PS: - Composition vs. Inheritance - it really depends on the target user of the application. If this is an intranet application where security is less of a problem and the user / browser environment is controlled, just use client-side validation, that is: composition.

+1
Aug 05 2018-11-11T00:
source share

I would prefer composition over inheritance.

In the case of your user password, it looks like you are actually storing the password in the Users table in clear text, which is VERY, VERY BAD.

You should only store the salted hash, and your EditUserModel should have two string properties to confirm the password and password, which are NOT fields in your table.

0
Aug 05 2018-11-11T00:
source share



All Articles