views:

391

answers:

5

Hi,

When I started using xVal for client-side validation, I was only implementing action methods which used domain model objects as a viewmodel or embedded instances of those objects in the viewmodel.

This approach works fine most of the time, but there are cases when the view needs to display and post back only a subset of the model's properties (for example when the user wants to update his password, but not the rest of his profile data).

One (ugly) workaround is to have a hidden input field on the form for each property that is not otherwise present on the form.

Apparently the best practice here is to create a custom viewmodel which only contains properties relevant to the view and populate the viewmodel via Automapper. It's much cleaner since I am only transferring the data relevant to the view, but it's far from perfect since I have to repeat the same validation attributes that are already present on the domain model object.

Ideally I'd like to specify the Domain Model object as a meta class via a MetaData attribute (this is also often referred to as "buddy class"), but that doesn't work since xVal throws when the metadata class has properties that are not present on the viewmodel.

Is there any elegant workaround to this? I've been considering hacking the xVal sourcecode, but perhaps there is some other way I have overlooked so far.

Thanks,

Adrian

Edit: With the arrival of ASP.NET MVC 2, this is not only a problem related to validation attributes anymore, but it also applies to editor and display attributes.

+3  A: 

We moved our validation attributes to the ViewModel layer. In our case, this provided a cleaner separation of concerns anyway, as we were then able to design our domain model such that it couldn't get into an invalid state in the first place. For example, Date might be required on a BillingTransaction object. So we don't want to make it Nullable. But on our ViewModel, we might need to expose Nullable such that we can catch the situation where the user didn't enter a value.

In other cases, you might have validation that is specific per page/form, and you'll want to validate based on the command the user is trying to perform, rather than set a bunch of stuff and ask the domain model, "are you valid for trying to do XYZ", where in doing "ABC" those values are valid.

Jimmy Bogard
So you are repeating validation attributes for views that handle changes to the the same business entities?
Adrian Grigore
Nope, we don't do validation attributes on business entities. Business entities are not allowed to get into an invalid state, so there's no need to put validation attributes on them.
Jimmy Bogard
Validation is a domain concern that changes with the domain, not with the UI. Putting this domain-specific logic in the UI obliterates that separation. At the most, ViewModels should validate "simple requirements" that are domain-agnostic and do not change with the domain. For example, required fields and email formatting. All other validation should be accepted (in most cases) as strictly server-side in the domain, without client-side autogenerated support.
gWiz
@gWiz: I think he meant that domain objects enforce their validity through either compile-time type safety or real-time input validation, as opposed to accepting invalid input and providing a way to get at the "errors". I tend to agree with this approach. There's a subtle difference between input validation and domain validation that's often forgotten in MVC.
Aaronaught
Whether you call it input validation or domain validation, the point is to prevent invalid input from being saved into the domain. My point is that you want to keep complex logic that is specific to the domain but generic across UI's, in the domain. I understand the goal of not allowing domain objects to ever get into an invalid state. This is supported out-of-box in ASP.NET MVC via DeafultModelBinder.SetProperty, which will catch exceptions thrown while attempting to set a property in the ModelState. Not sure how an extra view model layer would be any better.
gWiz
I tend to use methods to update members, rather than putting a lot of logic in property setters. That tends to force order of operations (StartDate needs to be less than EndDate, but what if I set EndDate first?) that becomes more and more opaque. Validation tends to center around specific commands, rather than entities. A state valid in one context may be invalid in the next.
Jimmy Bogard
A: 

I'm gonna risk the downvotes and state that there is no benefit to ViewModels (in ASP.NET MVC), especially considering the overhead of creating and maintaining them. If the idea is to decouple from the domain, that is indefensible. A UI decoupled from a domain is not a UI for that domain. The UI must depend on the domain, so you're either going to have your Views/Actions coupled to the domain model, or your ViewModel management logic coupled to the domain model. The architecture argument is thus moot.

If the idea is to prevent users from hacking malicious HTTP POSTs that take advantage of ASP.NET MVC's model binding to mutate fields they shouldn't be allowed to change, then A) the domain should enforce this requirement, and B) the actions should provide whitelists of updateable properties to the model binder.

Unless you're domain is exposing something crazy like a live, in-memory object graph instead of entity copies, ViewModels are wasted effort. So to answer your question, keep domain validation in the domain model.

gWiz
The UI depends on the domain. It doesn't have to depend *directly* on the domain. Pretty much any "report" is going to require a DTO; likewise, most forms may not map perfectly to a single domain object and therefore require a specialized UI instance. *The logic for DTO and ViewModel conversion is still part of the domain model.* At no point have you broken any abstractions or violated any invariants. WPF even crystallizes this principle as the MVVM (Model-View-ViewModel).
Aaronaught
I would argue that reporting is a separate domain from the business domain. A single UI can unify both domains (depend on both). And I have no qualms with composing multiple domain objects into a single class for the purpose of strongly-typed views. Putting ViewModels in the domain is a blatant violation of separation of concerns. Now you can't change your UI without changing your domain. And it's arguable that ViewModels can be reused between different apps/UIs that support different usage scenarios.
gWiz
Btw as I understand it, in WPF-specific parlance, a ViewModel is a model of the view. It's an abstraction of the characteristics of the view. It is therefore inherently defined in the UI. The point of such a class is to allow a view to bind to properties on the model that are not directly-mappable to a simple UI element (e.g. drop-down lists). But according to the pattern, the ViewModel actually composes the domain Model and performs binding transformations between the UI and the Model.
gWiz
gWiz: I fully agree that validation rules should ideally reside on the model and shoiuld not be duplicated in the ViewModel. However, simply not true that you don't need custom viewmodels at all. There are many instance in which you absolultely have to use a custom viewmodel if you want to keep your view type-safe. For sample ASP.NET MVC applications like NerdDinner this might not be the case, but it happens quite often in bigger projects with more complex user interface.
Adrian Grigore
I agree with @Adrian. I'm still pretty new to ASP.NET MVC, but I have already come across examples that required a `ViewModel`. For one: my `User` contains `Username` and `HashedPassword`, but my "reset password" view needs `Password` and `PasswordConfirmation`. There is no logical mapping between the view and the model here. I use a`ViewModel` to validate that the two passwords match and follow some other rules for length, etc., then a single password gets passed to the repository, where it is hashed and persisted in a database. How would this be done w/o a `ViewModel`?
DanM
Thansk for the comment, that's an interesting point. I have just used the ViewData dictionary and did validation in an ActionFilter. Confirmation of this sort was the only cases where I had UI-level validation in my previous project. Since these validations are unique to action methods, reusability of ViewModel approach was overkill IMHO. I'll explore the use of ViewModels for confirmation. I was thinking login validation might leverage a ViewModel as well, but that seems like shoe-horning something into the ASP.NET MVC validation model.
gWiz
If you are using ViewData then you can easily get away without custom viewmodels. But a proper viewmodel together with the strongly typed input helpers in ASP.NET MVC 2 is much cleaner and safer. Add compile-time view validation to this and you have much more robust views. It sure beats the ViewData dictionary.
Adrian Grigore
+2  A: 

If ViewModels are hypothetically being forced upon you, then I recommend that they only enforce domain-agnostic requirements. This includes things like "username is required" and "email is formatted properly".

If you duplicate validation from the domain models in the view models, then you have tightly coupled the domain to the UI. When the domain validation changes ("can only apply 2 coupon per week" becomes "can only apply 1 coupon per week"), the UI must be updated. Generally speaking, this would be awful, and detrimental to agility.

If you move the validation from the domain models to the UI, you've essentially gutted your domain and placed the responsibility of validation on the UI. A second UI would have to duplicate all the validation, and you have coupled two separate UI's together. Now if the customer wants a special interface to administrate the inventory from their iPhone, the iPhone project needs to replicate all the validation that is also found in the website UI. This would be even more awful than validation duplication described above.

Unless you can predict the future and can rule out these possibilities, only validate domain-agnostic requirements.

gWiz
+1  A: 

I don't know how this will play for client-side validation, but if partial validation is your issue you can modify the DataAnnotationsValidationRunner discussed here to take in an IEnumerable<string> list of property names, as follows:

public static class DataAnnotationsValidationRunner
{
     public static IEnumerable<ErrorInfo> GetErrors(object instance, IEnumerable<string> fieldsToValidate)
     {
           return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>().Where(p => fieldsToValidate.Contains(p.Name))
                  from attribute in prop.Attributes.OfType<ValidationAttribute>()
                  where !attribute.IsValid(prop.GetValue(instance))
                  select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
     }
}
Brandon Linton
While a similar fix could be implemented on the client side by branching xVal, I wouldn't do it this way since it relies on magic strings and it's all too easy to forget adding a string here after adding a new property to the model.
Adrian Grigore
I agree it's not a perfect solution, but we're managing the magic string issue through constant collections of field names, named after the step in our workflows they refer to. That way when the controller invokes the class that performs the validation during the post, it passes in the collection of fields you know you need validated for that view.
Brandon Linton