I'm using ASP.NET MVC 3 and Entity Framework 4.1 Code First.
Let's say I have a User
entity :
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 it in my UserController
I want to add a PasswordConfirmation
field and verify that PasswordConfirmation == Password
My first try was :
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 the client side validation works but (Edit: client side validation working was a coincidence.) doesn't work and the server side validation fails 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
Implementing 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 the server side validation works but the client side validation doesn't work anymore. Implementing IClientValidatable
seems a bit too complicated and I prefer not having client side validation in this case.
public class EditUserModel : User { [Compare("Password", ErrorMessage = "Passwords don't match.")] public string PasswordConfirmation { get; set; } }
When trying to directly save EditUserModel
using EF it doesn't work, I get some some error message about the EditUserModel
metadata so I'm using AutoMapper to convert from User
to EditUserModel
and backwards. This solution works but it more complex because I have to convert from the model to the view model and backwards.
(Suggested by Malte Clasen)
The view model would have all the properties of the model 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 like the least because of code duplication (DRY)
Questions
What are the pros and cons of inheritance, composition and duplication in this case ?
Is there a simple way to have both client side and server side validation without having to convert the model to the view model and backwards ?
Having struggled with this question before, I have in various instances gone with all three. In general, most of the opinions I've seen favor duplication in an MVC project, with a ViewModel constructed specifically for each view. In this manner the convention you'd use is something like UserDetailsViewModel
and UserCreateViewModel
. As you said, at that point AutoMapper or some other auto mapping tool would be used to convert from your domain objects to these flat ViewModels.
While I, too, don't like repeating code, I also don't like polluting my domain objects with validation or other view-specific attributes. Another advantage, though admittedly one almost nobody would ever have to contend with (regardless of what all the pros say), is that you can manipulate your domain objects in some ways without necessarily manipulating your ViewModels. I mention that because it's commonly cited, not because it carries much weight for me.
Lastly, using a truly flat ViewModel makes for cleaner markup. When I've used composition, I've often made errors creating HTML elements with names that are something like User.Address.Street
. A flat ViewModel reduces at least my likelihood of doing that (I know, I could always use HtmlHelper routines to create elements, but that's not always feasible).
My recent projects have also pretty much required separate ViewModels these days anyway. They've all been NHibernate-based, and the use of proxies on NHibernate objects makes it not possible to use them directly for views.
Update - here's a good article I've referred to in the past: http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With