15

Using this approach to view models in MVC: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

leaves an unanswered question in my mind. So it is about time I had it cleared up.

If I'm using auto mapper to map domain properties to a dto, then I appreciate that my domain layer can return a set of validation rules when the dto is mapped to a domain entity which is saved.

But, I don't see a DRY way of getting client validation to work and adding the errors to model state so they correspond to the correct property on the view model.

Cheers

Steven
  • 166,672
  • 24
  • 332
  • 435
nick
  • 1,477
  • 2
  • 20
  • 29
  • 1
    Good point. I have done so on my last couple of questions to try and remedy this. – nick Jan 10 '11 at 11:54

3 Answers3

11

I found a related question with some interesting responses:

Mapping Validation Attributes From Domain Entity to DTO

I've been thinking about this, and in a way it is analogous to the situation we have with server-side and client-side validation. (e.g. Using both NHibernate Validator and jQuery.validate).

These days it's pretty well accepted that you should have a full set of server-side validation, and adding client-side validation is an option you can choose in order to make your application more user-friendly. It used to be that you had to implement your client-side validation manually, but you accepted the duplication of work because of the benefit in usability.

I'd argue that what we're dealing with here is very similar. You should have validation in your domain layer. You can't rely on the consuming applications to always add the validation themselves.

You then have the option in your application of adding validation on your DTO/view models. You do this because it's more helpful to deal with validation errors in the view rather than letting them get through to the domain which could throw an exception or give a less helpful error message. The point is that from the domain perspective you don't rely on this being done. You're still confident in your system because you know if any bad data does get through, your model will catch it.

The client/server case is a non-issue these days because so much work has been done to automate it, generating the client-side code from the server-side code (e.g. ModelValidatorProvider in ASP.Net MVC). I believe that as more and more people take up the use of view models/DTOs we'll start to see similar solutions for mapping domain validation onto the DTOs automatically (it's already happening with AutoMapper).

So in short, my (pragmatic rather than ideal ;)) answer is:

Accept the violation of DRY for now, do validation in both places, and try to contribute to projects that aim to automate it in future

Community
  • 1
  • 1
James Morcom
  • 1,749
  • 14
  • 17
  • I'm giving this as the answer because you highlight the trade offs quite well and point out there is no universally-perfect solution. – nick Jan 11 '11 at 23:51
5

I prefer to put the Model as a property on the ViewModel along with other view-specific fields. That way it gets validated by MVC during binding and I can validate it on the backend when not being bound via MVC. This way the client validation gets provided by MVC and I get clean models with validation object not directly tied to a view.

public class MyViewModel
{
    public MyModel MyModel {get;set;}
    public bool IsSomethingAllowed {get;set;}
}

public class MyModel
{
    public int Id {get;set;}
    [Required]
    public string Name {get;set;}
}
Jab
  • 13,713
  • 2
  • 42
  • 48
  • 1
    this is a convention my team has decided on. For the projects we work on, losing DRY validation for the sake of loosely-coupled view models is not worth the trade off - it is less efficient and more prone to error. – nick Jan 10 '11 at 21:29
  • 1
    There are issues with using domain entities. e.g. (1) What if the type is incorrect e.g. not a valid integer? Any validation you have on your domain entity will not even get hit, the model binding will fail and you will get a generic message. With viewmodels, you can use string for all properties, ensuring that model binding works and then your viewmodel validation can pick up whether it is a valid integer and display a custom error message to the user. (2) If you are using resource files for validation messages, your domain objects will have to reference your resource file dll. Not very nice! – Paul Hiles Jan 11 '11 at 16:48
  • Not very nice I agree. But do you want enforce business rules in the model layer or not. This appears to be the main trade off. Good that you point this out though. – nick Jan 11 '11 at 23:46
  • 1
    Won't this be exposing domain properties which you don't want the user to view or set. Say an example a normal user cannot see a check box which says discount amount. But a super user can set this field. So, when this view model is posted the user can craft a request which can set the discount field even if not allowed. This may be a serious security violation. – Ramesh Apr 26 '14 at 15:11
2

A common approach is to put all your validation in your view models, typically by using data annotations. MVC allows you to automatically generate client-side (javascript) validation from the data annotations. Pretty DRY.

You controller post actions take in the viewmodel and check the IsValid property. In this way, you are validating at client and server with the same code (or should, I say attributes in the case of data annotations):

[HttpPost]
public ActionResult ResetPassword(ResetPasswordViewModel viewModel)
{
  if (ModelState.IsValid)
  {
    // convert to dto/entity and pass to next layer
    // redirect to success page
    return RedirectToAction("ResetPasswordSuccess");
  }
  // display original view which will display error messages
  return View();
}

Just to add that just by having the controller action take in the viewModel as a parameter, the default MVC model binder will automatically validate your viewmodel and add any errors to the ModelState errors collection which is used to display errors within your views.

Paul Hiles
  • 9,558
  • 7
  • 51
  • 76
  • 2
    To me that either isn't dry because I have to validate at the model and the VM - or it doesn't allow me to enforce my business rules at the model because I only add validation to the VM. – nick Jan 10 '11 at 14:51
  • I guess you're basically saying you've made the design decision that your domain will only ever be consumed by your web app, and that developers of your web app should always use view models rather than domain entities to perform CRUD actions? – James Morcom Jan 11 '11 at 10:18
  • i.e. You accept that the domain model is vunerable to bad data, because you know your developers won't use it that way? – James Morcom Jan 11 '11 at 10:23
  • @James, yes in my case the web app is the only consumer. The controllers take in viewmodels and validate using data annotations or fluent validation. viewmodels are converted to domain entities using automapper and passed to service. service can throws custom exceptions for any additional business rules which are then caught by action and added into ModelErrors collection. – Paul Hiles Jan 11 '11 at 16:17
  • 1
    You will find very different opinions on this. You might find this thread interesting - http://forums.asp.net/p/1502378/3558774.aspx – Paul Hiles Jan 11 '11 at 16:17