I followed these tutorials:
http://www.asp.net/learn/mvc/tutorial-39-cs.aspx
http://schotime.net/blog/index.php/2009/03/31/integrating-xval-validation-with-linq-to-sql/
in order to enforce data validation using Data Annotation for a LINQ-To-SQL-generated class. The metadata class looks like this:
[MetadataType(typeof(PositionValidation))]
public partial class Position
{
}
public class PositionValidation
{
[Required]
public string Title { get; set; }
}
The validation works correctly, but only if I do this in the controller:
if (ModelState.IsValid)
{
_positions.AddPosition(newPosition);
return RedirectToAction("List");
}
If I leave out the check for valid ModelState, it will attempt to add it to the database, even if Title is empty. As a result, I get an entry with a blank title (this applies to edits as well).
I was under the impression that data validation enforces it for the model side too, in addition to the controller/view. Does this mean that I have to add in additional code to do validation in the Position class too? If so, doesn't this violate DRY?
So in other words (please let me know if I am wrong), you expected that your action was NOT executed at all if the Data Annotation validation failed. This was the only way to omit the if(Model.IsValid) statement.
Your assumption is not correct, and this is by design. It's a very nice feature in fact, not a nuisance. You add only a line of code, to check whether there are errors, and in return you get the possibility to:
add your own errors which come from the business logic, so they are displayed to the user immediately, and not upon the next submission, when the DA is ok
reset errors or customize errors
redirect to some other view, or do any action (e.g. logging) upon certain conditions
Related
Just curious on your thoughts or experiences around service layer validation.
I have to process fairly standard validation such as "object with name property doesn't already exist", but I wasn't sure how to return these validation failures back to the controller.
My initial thought was to implement a standard List<ValidationError> but I've seen it done each and every way so was curious the pros/cons of each.
Thanks for any input.
If you go with System.ComponentModel.DataAnnotations entries you can (as you seem to know) decorate your properties with required and many more tags
public class Person
{
[Required(ErrorMessage="object with name property doesn't already exist")]
public string Name { get; set; }
}
although I personally use ViewModels rather than exposing domain mdoels to the view, your controller action can now do something like:
[HttpPost]
public ActionResult SavePerson(Person model)
{
if (ModelState.IsValid)
{
// your model validates - do things
return RedirectToAction("success view here");
}
return View(model);
}
This is one of the standard 'post' handler patterns in MVC. This is the simplest path to getting your object model validating in my opinion.
From there, there are a few other options - your domain object can implement IValidatedableObject and you can yield return the errors (see http://buildstarted.com/2010/09/20/mvc-3s-ivalidatableobject/ as an example).
I'd recommend not mixing the two though, as if you are using dataannotations and have even a single invalid property, the IsValid method on IValidatableObject will not be called.
From there, there's lots you can do with custom validation attributes (the extended version of IsValid seems to give you more flexibility http://msdn.microsoft.com/en-us/library/gg480674%28v=vs.98%29.aspx)
Hope some of the above helps - once you get past the basics there's a lot you can do with it and things like client validation of custom attributes etc. are all fun.
Cheers,
Terry
[edit to add:
After re-reading your post, it may be that you want to only validate at the service layer? If so, I've used the following approach:
public void Setname(string newName)
{
Validator.ValidateProperty(newName, new ValidationContext(this, null, null) { MemberName = "Name" });
Name = newName;
}
obviously your Name property would need a { get; private set; } for this, though you could always add the Validator.ValidateProperty into an extended setter for the public property either.
]
On a new project I'm working on (first time mvc) I've been using the ms code contracts (which throw exceptions) and do all the validation on my domain objects themselves. For things that can't be validated there (such as validations that require database access) I validate in my services and throw exceptions. Additionally like the poster above I have whole separate view models for everything that have data annotations validators on them. The exceptions bubble up and I catch them in the controller and append to the ModelState. There's a lot of overlap with those and the view model validation but it's not much extra effort and allows me to vary the validation per view and yet still have the "core" validations be required.
The book pro asp mvc 2 has another nice way - write a class that inherits Exception and contains a collection of errors. Then you do your validations, add to the collection then throw the exception then he catches it in the controller and copies over to ModelState. This method will let you catch ALL the errors in one exception instead of just one at the service layer.
I'm working on an ASP.NET MVC project that will allow users to perform batch edits on the attributes of objects. The implementation is in a sort of "wizard" like form with four phases to the process as follows:
"Select the attributes you want to edit" - the first page will present the user with a list of checkboxes representing each of the attributes they want to edit. The user should check the attributes they wish to edit and select "Continue".
"Edit the selected attributes" - the second page will present the user with a list of distinct "editors" which will be unique for each of the attributes they selected on the first page.
"Review your changes" - this page will allow the user to review the changes they've made to the attributes they selected.
"Submit your changes" - this page will actually submit the information about the edits the user wishes to make to the selected attributes against the selected collection of objects.
Fairly straight-forward.
As I mentioned, the "editor" will be unique to each attribute, and could have any combination of different controls on it. Once a user has made their edits and the application posts that information to the "Review" page is where I'm currently having my problem.
We've developed the concept of an "EditorWorker" class that is unique to each attribute, which is responsible for generating the ViewModel necessary for each editor, but is also responsible for creating/returning (within the "Review" page controller action) an object that is the "model" object for the editor that the post data can be bound to, which can then be use to display the edited data for review. This object should have properties that match up with the IDs of the controls in the editor so that model binding can occur.
I've got the "EditorWorker" creating and returning the class needed, but for some reason, when I call TryUpdateModel and pass in that class, its properties aren't getting populated as a result of that method call as I would expect them to. I have verified that the values are in the posted FormCollection. Below is the code for my controller action where I'm attempting to do this. If someone can help me understand why TryUpdateModel isn't working in this scenario, I would be very appreciative.
[HttpPost]
public virtual ActionResult Review(ReviewBatchViewModel model)
{
var selectedAttributes = GetSelectedAttributes(model.SelectedAttributeIds.Split(',').Select(i => Int64.Parse(i)).ToArray());
var workers = new List<IEditorWorker>();
var reviewData = new Dictionary<ViewAttribute, IEditData>();
foreach (var attribute in selectedAttributes)
{
if (!string.IsNullOrEmpty(attribute.EditorWorker)) // If there is no EditorWorker defined for this object, move on...
{
var worker = ServiceLocator.Current.GetInstance(Type.GetType(string.Format("{0}.{1}", EditorWorkerNamespace, attribute.EditorWorker)));
var attributeEditData = ((IEditorWorker)worker).LoadEditData();
if (TryUpdateModel(attributeEditData))
model.EditData.Add(attributeEditData); // model.EditData is a List<IEditData> that will be iterated on the Review page
reviewData.Add(attribute, attributeEditData);
}
}
return View(model);
}
// ReviewBatchViewModel.cs
public class ReviewBatchViewModel : BaseViewModel
{
public ReviewBatchViewModel() { EditData = new List<IEditData>(); }
public string SelectedAttributeIds { get; set; }
public List<ViewAttribute> SelectedAttributes { get; set; }
public List<IEditData> EditData { get; set; }
}
// IEditData.cs
public interface IEditData
{
}
// BroadcastStatusEditData.cs
public class BroadcastStatusEditData : IEditData
{
public int BroadcastStatus { get; set; }
}
I totally understand that this controller action is incomplete in its current state. I'm presently working on just trying to get those EditData objects populated correctly before I move on. As mentioned, any thoughts would be greatly appreciated. Thanks.
UPDATE: With regards to #mare's comment, I should have explained that part more clearly, sorry. The call to TryUpdateModel actually is returning true, but the fields on the model object being passed into it aren't actually being populated from the values that have been confirmed present in the posted form data. The model object being passed into the call is not a List, its just a poco. The resulting, ultimately hopefully populated model object is then being added to a List collection of model objects that will then be used for displaying the posted data for review on the Review page. I'm not loading anything from a datastore at all. Unique editors for each selected attribute are being rendered to the Edit screen, and I'm attempting to capture the edit values for display on a Review screen prior to submitting the batch of edits to a service. Hopefully that's more clear. Thanks.
UPDATE 2: I've included the definition of the ReviewBatchViewModel class as requested by #mare in the comments. The use of the var keyword in most cases in this code sample is largely due to the fact that the methods that are populating those variables is going to be returning an object of a different type for each attribute selected, so I never know exactly what its going to be at runtime (although it will always implement an interface, in this case either IEditorWorker and/or IEditData). There is a single class in the Model called "Attribute". The provided code sample has three variables relative that class: 1) SelectedAttributeIds is a comma-separated list of the Id's of the attributes that the user has selected to edit, which gets passed from the Edit page to the Review page via hidden field, 2) selectedAttributes is a collection of the actual Attribute objects that correspond to those Ids that I can work with, and 3) attributeEditData is an instance of the IEditData class specific to each given attribute that I'm attempting to bind the posted data from the Edit page to.
Hopefully this additional information clears things up even more.
TryUpdateModel is a generic method, and therefore attempts to infer all type information based on the Generic Type Parameter.
From what I understand in your example above, you are always passing in a IEditData correct?
In effect you are saying:
TryUpdateModel<IEditData>(attributeEditData)
This is most likely the cause for not seeing any properties being set, since IEditData doesn't have any properties ;)
To do what you want you will probably have to create a custom ModelBinder.
As a quick code review side note, your solution seems overly complicated. I had to stare at your solution for a good while just to figure out where to start. Creating a custom model binder may solve your immediate problem, but you might be looking at a big time maintenance headache here. I'm willing to bet there is a simpler approach that will lead to fewer problems down the road.
Based on your comments I have changed the code around from System.Object to your IEditData interface, but everything still holds. I noticed in an earlier comment you mentioned using var because you didn't know the type until runtime. However, there is nothing magic about the var keyword. The only thing it does is give you implicit typing, but it is still statically typed.
The nice thing about MVC is that you can just pop over to Codeplex and have a look at the source for TryUpdateModel if you want. Digging down a few layers you will eventually find a call to this internal method:
protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
if (model == null) {
throw new ArgumentNullException("model");
}
//valueProvider is passed into this internal method by
// referencing the public ControlerBase.ValueProvider property
if (valueProvider == null) {
throw new ArgumentNullException("valueProvider");
}
Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
//Binders is an internal property that can be replaced by
// referencing the static class ModelBinders.Binders
IModelBinder binder = Binders.GetBinder(typeof(TModel));
ModelBindingContext bindingContext = new ModelBindingContext() {
Model = model,
ModelName = prefix,
ModelState = ModelState,
ModelType = typeof(TModel),
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
binder.BindModel(ControllerContext, bindingContext);
return ModelState.IsValid;
}
Notice the use of typeof(TModel) everywhere... in your case that is getting translated into typeof(IEditData), which isn't very useful since it is only a marker interface. You should be able to adapt this code for your own use, making sure to use GetType() in order to get the actual type at runtime.
I hope this helps out!
P.S. I've added some comments to the above code to help out a little
#Josh, you were very helpful in helping me understand why TryUpdateModel wasn't working for me, and I appreciate that. Unfortunately, I think the larger issue here was that fact that I (not exactly sure which) was either unable or unwilling to try to document all of the details of the requirements for the problem I'm trying to solve here, which I think made it difficult for anyone to be able to provide much meaningful input. The biggest problem for us is that, because we have no idea until runtime which attributes a user has selected for editing, we don't know which objects we'll be working with in the context of these controller actions, or what their types will be. The one place that we safely can work with known data and types, is within the context of each of the unique EditorWorker objects, which is where I've chosen to do the heavy lifting here.
I was hoping and attempting to take advantage of all of the heavy lifting that MSFT has done for us within the MVC framework to handle model binding, but I've come to the conclusion at this point that I don't think that's going to work for us. The solution that I've come up with at this point, is to allow the LoadEditData method of the EditorWorker classes handle loading up the EditData classes for for me. As each EditorWorker class is unique to, and has knowledge of the attribute that it is associated with. The problem I was having originally was that I was letting the EditorWorker.LoadEditData method just return an empty instance of the specific type of EditData class that I needed for the attribute I was currently working with, and let the MVC framework handle model binding to that object for me. That wasn't working because that method is designed to return an object of type IEditData, and I never really knew exactly what type it was that I was currently working with, so I had no way of specifying the type in the call to either of the typed methods: TryUpdateModel<T> or UpdateModel<T>.
So the solution I've come up with, and am going with at least for now (re-education and/or refactoring may very well change this in the future, who knows) is to just pass the Request.Form object into the call to EditorWorker.LoadEditData and let that method handle actually loading up the EditData object that it knows it needs to return for the attribute it's responsible for, which it can do as it knows what information should be in the posted form collection to load up its EditData object.
So that's where I'm at for now. Thanks for the help.
Suppose I have this model:
public class ViewModel
{
[Required]
public string UserInput { get; set; }
[Required]
public Guid EntityId { get; set; }
}
Now, when UserInput is wrong, I want to re-display the same page but with validation errors (e.g. /Edit). However, when EntityId is wrong, I want to redirect to some other page (e.g. /Create).
I can do this manually inside each controller...
if (!ModelState.IsValidField("EntityId")) { redirect }
//or
if (string.IsNullOrEmpty(data.EntityId)) { redirect }
but it's kind of boring and violates DRY. Imaging several entities, nested view models with entities... too cumbersome. I'd better have something like ModelState.IsValidUserData and ModelState.IsValidCriticalData. But there's no such thing.
Now, EntityId is actually bound using my custom model binder, that knows that it is mission-critical. So there's this solution:
Usual fields do populate ModelState with errors as usual.
(a) Critical fields are bound using custom model binder that throws special "CriticalModelErrorException". Controller actions have [HandleCrirticalError("action", "controller')] attribute - which handles critical errors and redirects to the given action.
(b) Critical fields are bound using custom model binder that sets BaseController.CriticalModelErrors property (obviously all controllers are derived from supercontroller base class). Each action is free to check both ModelState.IsValid and base.CriticalModelErrors and behave freely based on that.
(c) Critical fields are bound using custom model binder that sets special-format model state errors, e.g. AddModelError(name, "!CRITICAL! text"; Then base controller have method that detects such strings.
2a example:
[HandleCriticalError("Create")] // uses the same controller
[HandleModelStateError("Edit")] // redisplays page with validation errors
public ActionResult Edit(ViewModel data)
{
// here we know both our data entities and user data are valid and safe
}
2b example
public ActionResult Edit(ViewModel data)
{
if (!ModelState.IsValid)
return View(data);
if (base.CriticalModelErrors.Count > 0)
return RedirectToAction("Create");
// here we know both our data entities and user data are valid and safe
}
2c example
protected bool HasCriticalErrors()
{
return ModelState.Any(x => x.Value.Errors.Any(x => x.ErrorMessage.StartsWith("!CRITICAL!")))
}
// then same as 2b
Now, the questions: how it's handled by other apps and developers (you and your apps)? Which one would you prefer? Are there any drawbacks or better solutions?
Use the one that introduces the most amount of automation for you, so you won't have to repeat the same code on my places. As you have done already I'd go with 2a but make it a bit different so I wouldn't put those attributes on every single action but rather on the whole Controller class. If at all possible (if required by at least majority actions). If you need to exclude certain actions, create a separate action filter that will disable redirecting.
I've got a simple address entry app that I'm trying to use the IDataErrorInfo interface as explained on the asp.net site.
It works great for items that can be validated independently, but not so well when some items depend on others. For example, validating the postal code depends on the country:
private string _PostalCode;
public string PostalCode
{
get
{
return _PostalCode;
}
set
{
switch (_Country)
{
case Countries.USA:
if (!Regex.IsMatch(value, #"^[0-9]{5}$"))
_errors.Add("PostalCode", "Invalid Zip Code");
break;
case Countries.Canada:
if (!Regex.IsMatch(value, #"^([a-z][0-9][a-z]) ?([0-9][a-z][0-9])$", RegexOptions.IgnoreCase))
_errors.Add("PostalCode", "Invalid postal Code");
break;
default:
throw new ArgumentException("Unknown Country");
}
_PostalCode = value;
}
}
So you can only validate the postal code after the country has been set, but there seems to be no way of controlling that order.
I could use the Error string from IDataErrorInfo, but that doesn't show up in the Html.ValidationMessage next to the field.
For more complex business rule validation, rather than type validation it is maybe better to implement design patterns such as a service layer. You can check the ModelState and add errors based on your logic.
You can view Rob Conroys example of patterns here
http://www.asp.net/learn/mvc/tutorial-29-cs.aspx
This article on Data Annotations ay also be useful.
http://www.asp.net/learn/mvc/tutorial-39-cs.aspx
Hope this helps.
Here's the best solution I've found for more complex validation beyond the simple data annotations model.
I'm sure I'm not alone in trying to implement IDataErrorInfo and seeing that it has only created for me two methods to implement. I'm thinking wait a minute - do i have to go in there and write my own custom routines for everything now from scratch? And also - what if I have model level things to validate. It seems like you're on your own when you decide to use it unless you want to do something like this or this from within your IDataErrorInfo implementation.
I happened to have the exact same problem as the questioner. I wanted to validate US Zip but only if country was selected as US. Obviously a model-level data annotation wouldn't be any good because that wouldn't cause zipcode to be highlighted in red as an error. [good example of a class level data annotation can be found in the MVC 2 sample project in the PropertiesMustMatchAttribute class].
The solution is quite simple :
First you need to register a modelbinder in global.asax. You can do this as an class level [attribute] if you want but I find registering in global.asax to be more flexible.
private void RegisterModelBinders()
{
ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
}
Then create the modelbinder class, and write your complex validation. You have full access to all properties on the object. This will run after any data annotations have run so you can always clear model state if you want to reverse the default behavior of any validation attributes.
public class AddressModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
// get the address to validate
var address = (Address)bindingContext.Model;
// validate US zipcode
if (address.CountryCode == "US")
{
if (new Regex(#"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).
Match(address.ZipOrPostal ?? "").Success == false)
{
// not a valid zipcode so highlight the zipcode field
var ms = bindingContext.ModelState;
ms.AddModelError(bindingContext.ModelName + ".ZipOrPostal",
"The value " + address.ZipOrPostal + " is not a valid zipcode");
}
}
else {
// we don't care about the rest of the world right now
// so just rely on a [Required] attribute on ZipOrPostal
}
// all other modelbinding attributes such as [Required]
// will be processed as normal
}
}
The beauty of this is that all your existing validation attributes will still work - [Required], [EmailValidator], [MyCustomValidator] - whatever you have.
You can just add in any extra code into the model binder and set field, or model level ModelState errors as you wish.
Please note that for me an Address is a child of the main model - in this case CheckoutModel which looks like this :
public class CheckoutModel
{
// uses AddressModelBinder
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
// etc.
}
That's why I have to do bindingContext.ModelName+ ".ZipOrPostal" so that the model error will be set for 'BillingAddress.ZipOrPostal' and 'ShippingAddress.ZipOrPostal'.
PS. Any comments from 'unit testing types' appreciated. I'm not sure about the impact of this for unit testing.
Regarding the comment on Error string, IDataErrorInfo and the Html.ValidationMessage, you can display object level vs. field level error messages using:
Html.ValidationMessage("address", "Error")
Html.ValidationMessage("address.PostalCode", "Error")
In your controller decorate the post method handler parameter for the object with [Bind(Prefix = "address")]. In the HTML, name the input fields as such...
<input id="address_PostalCode" name="address.PostalCode" ... />
I don't generally use the Html helpers. Note the naming convention between id and name.
Related: What’s the best way to implement field validation using ASP.NET MVC?
Let's suppose a solution with the following projects:
Foo; // the MVC web project
Foo.Models;
Foo.Repositories;
Foo.Services;
Foo.Models is the domain of the application with all the entities, doesn't matter if using EF, NH, POCO or whatever. Here's an example:
public class User
{
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
In Foo.Repositories there is a UserRepository and in Foo.Services there is a UserService.
In the web application let's consider a model binder like following:
public class UserBinder : DefaultModelBinder
{
//...
}
I see three different options on where to put the validation:
In Foo.Models like the following:
public class User
{
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public ICollection<KeyValuePair<string, string>> ValidateErrors()
{
//Validate if Username, Email and Password has been passed
}
}
In Foo.Services like:
public class UserService
{
public ICollection<KeyValuePair<string, string>> ValidateErrors()
{
//Validate if Username, Email and Password has been passed
}
}
In Foo inside the model binder:
public class UserBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var user = (User)bindingContext.Model;
// validate everything here
base.OnModelUpdated(controllerContext, bindingContext);
}
}
Another thing to notice is that considering the first 2 options [Model and Service] there is another decision to make: ValidateErrors method can be called directly on the controller or inside the Binder.
I have 2 questions on the scenario:
Should the validation be:
In the Model being called from the controller?
In the Model being called from the binder?
In the Service being called from the controller?
In the Service being called from the binder?
Directly in the Binder?
Any other idea?
All the above scenario discuss about the User creation. But what about User logon?
Let's say user uses the username and password to login in the application, so it won't need to validate the e-mail.
Where this validation should be?
In the Model being called from the controller?
In the Service being called from the controller?
Any other idea?
Check out the ASP.NET MVC Contact Manager Sample Application it has a very good architecture im my opinion
http://www.asp.net/learn/mvc/tutorial-26-cs.aspx'>http://www.asp.net/learn/mvc/tutorial-26-cs.aspx
I'm a big fan of putting calling the validation from the controllers and having the validation routine return an ActionResult so the controller can know what to do with the result.
For what it's worth, here's what I have scrounged up in my current project:
I have Models, Repositories (you can call them Services if you like), and ViewModels. I try to avoid writing custom model binders because (a) it's boring and (b) a strange place to put validation, IMHO. To me, a model binder is just taking items from the request and shoving them into an object. PHP, for example, doesn't do any validation when plucking items from a header into the $_POST array; it's the thing we plug the array into that cares about its contents.
My Model objects generally never allow themselves to enter an invalid state. This means that required parameters are passed in during constructors and properties will throw exceptions if they're attempted to be set with invalid values. And, in general, I try to design my Model objects to be immutable. For example, I have an Address object for mailing addresses that is constructed with an AddressBuilder object with looks at the field requirements for a given country by inspecting an AddressScheme that can be retrieved from the AddressSchemeRepository. Phew. But I think it's a good example because it takes something conceptually simple ("validate a mailing address") and makes it complicated in real world usage ("we accept addresses from over 30 countries, and those formatting rules are sitting in a database, not in my code").
Since constructing this Model object is kind of a pain--as well it should be, since it's being quite particular about the data that gets loaded into it--I have a, say, InputAddressViewModel object that my view binds to. The InputAddressViewModel implements IDataErrorInfo so that I get ASP.NET MVC's DefaultModelBinder to add errors to the ModelState automatically. For simple validation routines that I know ahead of time (phone number formatting, first name required, e-mail address format), I can implement these right in the InputAddressViewModel.
The other advantage of having a view model is that because it is shamelessly tailored to a particular view, your real model is more reusable because it doesn't have to make any weird concessions to make it suitable for UI display (e.g., needs to implement INotifyPropertyChanged or Serializable or any of that mess).
Other validation errors about the address I won't know about until I interact with my AddressScheme in my actual Model. Those errors will be there controller's job of orchestrating into the ModelState. Something like:
public ActionResult InputAddress(InputAddressViewModel model)
{
if (ModelState.IsValid)
{
// "Front-line" validation passed; let's execute the save operation
// in the our view model
var result = model.Execute();
// The view model returns a status code to help the
// controller decide where to redirect the user next
switch (result.Status)
{
case InputAddressViewModelExecuteResult.Saved:
return RedirectToAction("my-work-is-done-here");
case InputAddressViewModelExecuteResult.UserCorrectableError:
// Something went wrong after we interacted with the
// datastore, like a bogus Canadian postal code or
// something. Our view model will have updated the
// Error property, but we need to call TryUpdateModel()
// to get these new errors to get added to
// the ModelState, since they were just added and the
// model binder ran before this method even got called.
TryUpdateModel(model);
break;
}
// Redisplay the input form to the user, using that nifty
// Html.ValidationMessage to convey model state errors
return View(model);
}
}
The switch may seem repulsive, but I think it makes sense: the view model is just a plain old class and doesn't have any knowledge of the Request or the HttpContext. This makes the logic of the view model easy to test in isolation without resorting to mocking and leaves the controller code left to, well, control by interpreting the model's result in a manner that makes sense on a Web site--it could redirect, it could set cookies, etc.
And the InputAddressViewModel's Execute() methods looks something like (some people would insist on putting this code into a Service object that the controller would call, but to me the view model will do so much finagling of the data to make it fit the real model that it makes sense to put it here):
public InputAddressViewModelExecuteResult Execute()
{
InputAddressViewModelExecuteResult result;
if (this.errors.Count > 0)
{
throw new InvalidOperationException(
"Don't call me when I have errors");
}
// This is just my abstraction for clearly demarcating when
// I have an open connection to a highly contentious resource,
// like a database connection or a network share
using (ConnectionScope cs = new ConnectionScope())
{
var scheme = new AddressSchemeRepository().Load(this.Country);
var builder = new AddressBuilder(scheme)
.WithCityAs(this.City)
.WithStateOrProvinceAs(this.StateOrProvince);
if (!builder.CanBuild())
{
this.errors.Add("Blah", builder.Error);
result = new InputAddressViewModelExecuteResult()
{
Status = InputAddressViewModelExecuteStatus
.UserCorrectableError
};
}
else
{
var address = builder.Build();
// save the address or something...
result = new InputAddressViewModelExecuteResult()
{
Status = InputAddressViewModelExecuteStatus.Success,
Address = address
};
}
}
return result;
}
Does this make sense? Is it a best practice? I have no idea; it's certainly verbose; it's what I just came up with in the past two weeks after thinking about this problem. I think you're going to have some duplication of validation--your UI can't be a complete imbecile and not know what fields are required or not before submitting them to your model/repositories/services/whatever--otherwise the form could simply generate itself.
I should add that the impetus for this is that I've always kind of detested the Microsoft mentality of "set one property -> validate one property" because nothing ever works like that in reality. And you always end up getting an invalid object persisted because someone forgot to call IsValid or some such on the way to the data store. So another reason for having a view model is that it tailors itself to this concession so we get a lot of CRUD work of pulling items from the request, validation errors in the model state, etc quite easily without having to compromise the integrity of our model itself. If I have an Address object in hand, I know it's good. If I have an InputAddressViewModel object in hand, I know I need to call it's Execute() method to get that golden Address object.
I'll look forward to reading some of the other answers.
After a lot of research I think I got the answers to my question so i decided to share.
The validation code should be on Model.
As per the idea of "thin controller, fat model" AND considering that a model would know what it needs to validate or not.
For example, let's say I decide to user the Foo.Models in other solution but I decide NOT to use any other project and the validation is in other project.
I'll have to re-code the entire validation in this case what is a total waste of time, right?
OK. The validation code must be in the model but where should it be called?
This validation must be called where you're saving it to your database or file.
As in the proposed scenario I'm considering the repository as a domain, then we should consider putting the validation just before the change saving [in this example I'm using Entity Framework but it's not necessary, it's just to show]:
public class UserRepository : IRepository<User>
{
public void Create(User user)
{
user.Validate();
var db = dbFooEntities();
db.AddToUsers(user);
db.SaveChanges();
}
}
As per MS recommendation, the model validation should raise an exception and the controller must populate the ModelState with the errors found [I'll try to update this answer with a sample code on that as soon as I finish my app].
With that we have an answer for question #1.
What about question #2, regarding the login validation?
As login is not a situation where you're persisting your data, the validation should stay on the Service since logging in is a service in this case.
So, the answers for the question are:
In the Model being called from the REPOSITORY [that is called by the controller]
In the Service being called from the controller
This is very interesting and it helps me a lot in deciding where to put validation.
currently I feel the most for each model implementing a "Validate" method, which is called from a Repository or a Service.
However, what about validating if a chosen username is unique?
Should that code be inside the User model, or inside the UserService class, or in the UserRepository class?
If the uniqueness validation should be inside the User model, then the User model should have access to either the UserService or the UserRepository class. Is that OK, or is that against any "best practice" pattern?
For example:
class User
{
string Username { get; set; }
string Email { get; set; }
string Password { get; set; } // hashed and salted of course :)
IEnumerable<RuleViolation> Validate()
{
List<RuleViolation> violations = new List<RuleViolation>();
IUserService service = MyApplicationService.UserService; // MyApplicationService is a singleton class, especialy designed so that the User model can access application services
// Username is required
if ( string.IsNullOrEmpty(Username) )
violations.Add(new RuleViolation("Username", "Username is required"));
// Username must be unique: Should uniqueness be validated here?
else if( !service.IsUsernameAvailable(Username)
violations.Add(new RuleViolation("Username", "Username is already taken!"));
// Validate email etc...
return violations;
}
}
interface IUserRepository
{
void Save(User item);
}
interface IUserService
{
IUserRepository UserRepository { get; }
void Save(User item);
}
class UserService : IUserService
{
public UserService(IUserRepository userRepository)
{
this.UserRepository = userRepository;
}
IUserRepository UserRepository { get; private set}
public void Save(User user)
{
IEnumerable<RuleViolation> violations = user.Validate();
if(violations.Count() > 0)
throw new RuleViolationException(violations); // this will be catched by the Controller, which will copy the violations to the ModelState errors collection. But the question is, should we validat the user here, or in the UserRepository class?
UserRepository.Save(user);
}
}
class UserRepository : IUserRepository
{
void Save(User item)
{
IEnumerable<RuleViolation> violations = user.Validate();
if(violations.Count() > 0)
throw new RuleViolationException(violations); // this will be catched by the Controller, which will copy the violations to the ModelState errors collection. But the question is, should we validate the user here, or in the UserService class?
UserRepository.Save(user);
}
}
My guess would be that validation should be as close to the model as possible. So I'd say that the UserRepository should be the one responsible for validating it's model being added.
The most important queston for me is: Should the User model know about the IUserService / IUserRepository interfaces so that it can validate the Username uniqueness?
Or should the IUserService service validate uniqueness?
I'm curious about your views on this!
I'm using the DataAnnotations attributes in combination with a MVC model binder to do my validation and its pretty awesome. Since I treat User input as Command View Models its the cleanest way to keep domain clean from outside concerns.
http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html
This also allows me to take advantage of AutoForm by LosTechies.com:
http://www.lostechies.com/blogs/hex/archive/2009/06/17/opinionated-input-builders-part-8-the-auto-form.aspx
And I expect the client side validation tools in MVC 2, VS 2010 to take advantage of these attributes as well.
So I'm whipping out user input view models, commands, at a furious pace right now and tying them into not only the AutoForm functionality but my own custom UI templates to get AutoGrid and AutoOutput from these attributes as well.
Nothing is better than saying:
Html.AutoForm(Model);
Or
Html.AutoGrid(Model.Products);
And getting validation and html generation in a very DRY and orthogonal way. My controllers are light, my domain pristine, and my time is unoccupied by writing the same if( string.IsNullOrEmpty() ) method on every object with a FirstName property.
For me the approach was not as "philosophical" as others have written about. I'm trying to be very pragmatic about MVC development and I get a ton of bang for the buck out of these bits.