Class-Level Model Validation with EF Code First and ASP.NET MVC 3

Earlier this week the data team released the CTP5 build of the new Entity Framework Code-First library. 

In my blog post a few days ago I talked about a few of the improvements introduced with the new CTP5 build.  Automatic support for enforcing DataAnnotation validation attributes on models was one of the improvements I discussed.  It provides a pretty easy way to enable property-level validation logic within your model layer.

You can apply validation attributes like [Required], [Range], and [RegularExpression] – all of which are built-into .NET 4 – to your model classes in order to enforce that the model properties are valid before they are persisted to a database.  You can also create your own custom validation attributes (like this cool [CreditCard] validator) and have them be automatically enforced by EF Code First as well.  This provides a really easy way to validate property values on your models.  I showed some code samples of this in action in my previous post.

Class-Level Model Validation using IValidatableObject

DataAnnotation attributes provides an easy way to validate individual property values on your model classes. 

Several people have asked - “Does EF Code First also support a way to implement class-level validation methods on model objects, for validation rules than need to span multiple property values?”  It does – and one easy way you can enable this is by implementing the IValidatableObject interface on your model classes.

IValidatableObject.Validate() Method

Below is an example of using the IValidatableObject interface (which is built-into .NET 4 within the System.ComponentModel.DataAnnotations namespace) to implement two custom validation rules on a Product model class.  The two rules ensure that:

  • New units can’t be ordered if the Product is in a discontinued state
  • New units can’t be ordered if there are already more than 100 units in stock

We will enforce these business rules by implementing the IValidatableObject interface on our Product class, and by implementing its Validate() method like so:

image

The IValidatableObject.Validate() method can apply validation rules that span across multiple properties, and can yield back multiple validation errors. Each ValidationResult returned can supply both an error message as well as an optional list of property names that caused the violation (which is useful when displaying error messages within UI).

Automatic Validation Enforcement

EF Code-First (starting with CTP5) now automatically invokes the Validate() method when a model object that implements the IValidatableObject interface is saved.  You do not need to write any code to cause this to happen – this support is now enabled by default.

This new support means that the below code – which violates one of our above business rules – will automatically throw an exception (and abort the transaction) when we call the “SaveChanges()” method on our Northwind DbContext:

image

In addition to reactively handling validation exceptions, EF Code First also allows you to proactively check for validation errors.  Starting with CTP5, you can call the “GetValidationErrors()” method on the DbContext base class to retrieve a list of validation errors within the model objects you are working with.  GetValidationErrors() will return a list of all validation errors – regardless of whether they are generated via DataAnnotation attributes or by an IValidatableObject.Validate() implementation. 

Below is an example of proactively using the GetValidationErrors() method to check (and handle) errors before trying to call SaveChanges():

image

ASP.NET MVC 3 and IValidatableObject

ASP.NET MVC 2 included support for automatically honoring and enforcing DataAnnotation attributes on model objects that are used with ASP.NET MVC’s model binding infrastructure.  ASP.NET MVC 3 goes further and also honors the IValidatableObject interface.  This combined support for model validation makes it easy to display appropriate error messages within forms when validation errors occur. 

To see this in action, let’s consider a simple Create form that allows users to create a new Product:

image

We can implement the above Create functionality using a ProductsController class that has two “Create” action methods like below:

image

The first Create() method implements a version of the /Products/Create URL that handles HTTP-GET requests - and displays the HTML form to fill-out.  The second Create() method implements a version of the /Products/Create URL that handles HTTP-POST requests - and which takes the posted form data, ensures that is is valid, and if it is valid saves it in the database.  If there are validation issues it redisplays the form with the posted values. 

The razor view template of our “Create” view (which renders the form) looks like below:

image

One of the nice things about the above Controller + View implementation is that we did not write any validation logic within it.  The validation logic and business rules are instead implemented entirely within our model layer, and the ProductsController simply checks whether it is valid (by calling the ModelState.IsValid helper method) to determine whether to try and save the changes or redisplay the form with errors. The Html.ValidationMessageFor() helper method calls within our view simply display the error messages our Product model’s DataAnnotations and IValidatableObject.Validate() method returned. 

We can see the above scenario in action by filling out invalid data within the form and attempting to submit it:

image

Notice above how when we hit the “Create” button we got an error message.  This was because we ticked the “Discontinued” checkbox while also entering a value for the UnitsOnOrder (and so violated one of our business rules). 

You might ask – how did ASP.NET MVC know to highlight and display the error message next to the UnitsOnOrder textbox?  It did this because ASP.NET MVC 3 now honors the IValidatableObject interface when performing model binding, and will retrieve the error messages from validation failures with it.

The business rule within our Product model class indicated that the “UnitsOnOrder” property should be highlighted when the business rule we hit was violated:

image

Our Html.ValidationMessageFor() helper method knew to display the business rule error message (next to the UnitsOnOrder edit box) because of the above property name hint we supplied:

image

Keeping things DRY

ASP.NET MVC and EF Code First enables you to keep your validation and business rules in one place (within your model layer), and avoid having it creep into your Controllers and Views. 

Keeping the validation logic in the model layer helps ensure that you do not duplicate validation/business logic as you add more Controllers and Views to your application.  It allows you to quickly change your business rules/validation logic in one single place (within your model layer) – and have all controllers/views across your application immediately reflect it.  This help keep your application code clean and easily maintainable, and makes it much easier to evolve and update your application in the future.

Summary

EF Code First (starting with CTP5) now has built-in support for both DataAnnotations and the IValidatableObject interface.  This allows you to easily add validation and business rules to your models, and have EF automatically ensure that they are enforced anytime someone tries to persist changes of them to a database. 

ASP.NET MVC 3 also now supports both DataAnnotations and IValidatableObject as well, which makes it even easier to use them with your EF Code First model layer – and then have the controllers/views within your web layer automatically honor and support them as well.  This makes it easy to build clean and highly maintainable applications.

You don’t have to use DataAnnotations or IValidatableObject to perform your validation/business logic.  You can always roll your own custom validation architecture and/or use other more advanced validation frameworks/patterns if you want.  But for a lot of applications this built-in support will probably be sufficient – and provide a highly productive way to build solutions.

Hope this helps,

Scott

P.S. In addition to blogging, I am also now using Twitter for quick updates and to share links. Follow me at: twitter.com/scottgu

34 Comments

  • Any advice regarding how to implement a "by use case" validation without filling that Validate methods with poorly maintainable "if"s?

  • Scott, if we were t use the concepts above and build the validation into the model, is there anyway this could be easily reused if we had to build view models for each view?

    Suppose we have a sufficiently complex application where the domain model is shared between applications, so we decide to build localised view models for our views to adapt the big Models view of the world to how our application requires it.

    Would there be anyway to get the validations to flow from the big Model to the local view models without having to duplicate them or write infrastructure code to make it happen?

    Haven't tried it yet, was just wondering.

    Thanks

    Andy

  • Anything that lowers the entry level to writing validation is good news.

  • Do you have any example of validation, where validation rules are stored in database and not hard coded, because every organization can have different rules for the same entity

  • What about client-side validation? I don't suppose that get generated too. What is the recommanded practice to do it?

  • > ... enables you to keep your validation and business rules in one place

    Really? How can I implement a simple real-life rule like unique Email on an object using MVC/EF validation? All of this looks like misses one serious thing. Validation almost always depends on the context (other objects in DB, other rules, etc.). So from my point validator should be a separate class/object, or even set of validator classes for different cases, so that it can be used to validate an entity (to save, to insert, or other actions not related with SaveChanges). We can see really good examples of such approach in other frameworks (e.g. python's FormEncode library).

    Of course there should be DataAnnotations and such Validateme methods. But this _should_ be a syntax sugar over a more abstract validation mechanism. So that rules of validation should be really moved from controllers to business model layer.

    Thank

  • Indeed, seems that model-level validation to view model-level validation propagation stays a pain-point. But I hope that I'm wrong.

  • Really nice article as usual.

    Off-topic for Scott => I know it's not something about this article, but I would like to know if someday we will have some info about how to create the best localization with an MVC3 application.

    Thanks

    Nordès

  • 2 Laurent B-Roy
    I think client side validation you will need to do again on client side, as I know client side validation works on IDataErrorInfo interface..

  • It seems that the Validate method does not fire if there are any Data Annotation errors generated. When a defined Range attribute failed, the Validate method was not invoked. Is this intened or a bug?
    Thanks.

  • Excellent news! I'm sure that every person who has read this article has rolled their own way to do this, as it's a very obvious feature. I'm glad that it's now becoming part of the framework.

    @John T.:
    I believe you'll have to validate from the client using an AJAX call to take advantage of the IValidatableObject interface. Perhaps take a look at the xVal library (which, in turn, uses the jQuery Validation library). This has build in support for AJAX calls to server-side validation logic. However, I believe that xVal doesn't support IValidatableObject yet, so you'd have to modify it a bit.

  • Excellent news! I'm sure that every person who has read this article has rolled their own way to do this, as it's a very obvious feature. I'm glad that it's now becoming part of the framework.

    @John T.:
    I believe you'll have to validate from the client using an AJAX call to take advantage of the IValidatableObject interface. Perhaps take a look at the xVal library (which, in turn, uses the jQuery Validation library). This has build in support for AJAX calls to server-side validation logic. However, I believe that xVal doesn't support IValidatableObject yet, so you'd have to modify it a bit.

  • Would like to see the fluent API also add validation to the model.

  • Why in the world would an object validate itself? I mean, how hard would it have been to write IValidate? Then you could have small classes that each are responsible for validating one thing.

    How many responsibilities should one class have?

    It's a real shame that the EF team would make such a obvious error in good design.

  • Thanks for the update, Scott, but I'm wondering why IValidatableObject was added to data annotations. Can't the same thing be done with custom ValidationAttribute classes attached to the class instead of the properties?

  • Is there a way to do class level validation with mulitple properties using attributes as well? If so, is there an advantage to doing it that way, or using IValidatableObject? Which one is easier? This way seems quite straight forward and nice, but, I was thinking maybe doing it using attributes might be good also for consistency if you are using attributes for validating other single attributes.

    Also, I've noticed you use an instance variable for the EF Context in the Controller in this and other examples. Since DbContext is an IDisposable. I'm wondering if that would cause any issues as far as memory leaks or connection pool leaks go? A pattern that I've been using in a lot of my apps is to store the context in a local variable and use a using statement for disposing of it properly, but, I'd rather just put it in an instance variable like you did assuming it won't cause any problems. I just wanted to double check that and make sure it wasn't just being done that way for illustrative purposes.

  • First, thanks Scott for this short and direct blog post.

    I've been busy with MVC3 and validation using custom/configurable validation rules for a few days now and have not (yet) seen any limitations that prevents me from implementing this.
    The key for me has been to implement my own ModelValidatorProvider to handle property level validation on both client and server - no changes required to the Views since the 'data-' are generated for JQuery to do the client side validation.

    For Class - level validation (on the server) I simply follow the model Scott outlines by having a partial class that implements IValidatableObject.

    The 'metadata' that contains the validation rules are stored in a dB and are simply 'exposed' and used in the different classes mentioned above. Other factors like users, locale and settings are also taking into account.

    So in short, I believe we can have one set of Validation that are executed on 'all' layers, but it requires some work by implementing your own providers.
    Suggest looking into: http://dotnetslackers.com/articles/aspnet/Customizing-ASP-NET-MVC-2-Metadata-and-Validation.aspx

  • Scott,

    How about a real world example such as checking for a duplicate ids and displaying a list of available ids if there is a duplicate? That would be a bit more helpful...

    So let's see a couple month ago the best practice was jquery in the client for things like range checking and basic is empty check, now that EF CTP whatever is out did the best practice change to the EF model? Seems like there might be some overhead with this approach for such a trivial tasks...Wouldn't a light weight middle business object be "BEST" implementation for simple stuff like this.



  • This is a very useful approach to validating user forms, haven't been over here in a while you always have good content up ...

  • @Adam Boddington,
    I was just wondering same thing, all these can be done using ValidationAttribute even for cross level validation. If yo happen to see the sample code when creating mvc 2 vs designer creates one attribute named PropertyMustMathAttribute that serves the same purpose of IvalidatableObject.

  • @Adam Boddington,
    I was just wondering same thing, all these can be done using ValidationAttribute even for cross level validation. If yo happen to see the sample code when creating mvc 2 vs designer creates one attribute named PropertyMustMathAttribute that serves the same purpose of IvalidatableObject.

  • Wish there was a way to plug ValidatableObject into our PURE POCO classes without the need to implement IValidatableObject interface to prevent changes to our POCO Model classes. (a solution to migrate "Property and Class Level Validation Logic" to another class to keep our Model classes as simple as possible).

  • This model may be sufficient for basic scenarios but for real-life applications I consider this very bad design and support the opinion of Evgeny and Elliott. Validation should be done in a separate class since it is often dependent on the context (e.g. specific database state). Implementing and supporting something like IValidator would have been a better approach.

  • Any improvemnts in the Validation is great news. One thing I didn't like though, is how you tell the ValidationResult the fields that are affected: strings?! So if change a property name you also have to remember changing your ValidationResult. It's not a big deal, but I believe there might be better ways of doing that.

    Another thing, not from this post but about how the Validation localizes the error messages. I don't see much DRY in it =/

  • I said only things that I thought they could be improved in the previous post and forgot telling you that how great I think your blog is. Your blog is a place where I learn a lot of cool things and I need to thank you for that! Keep your good work.

  • How would you do that with vb.net?

  • @Mark Gordon (and others):

    Validation should always be done at the server, but can optionally be done at the client if you want to avoid a roundtrip.

    You should never validate only on the client and never at the server. Doing so exposes your server to abuse because any app can HTTP POST some data at your web service/site - if you don't validate at the server, your database can get right royally screwed.

    Again: ALWAYS validate at the server and validate at your client if you want to avoid a roundtrip.

  • I think it's brilliant. This is a big chunk of my own code I can cut out and throw away from a previous project. It's great to have EF and MVC working together this way. Don't bother with the nay-sayers here. Some of them obviously haven't been following your blog, and other's aren't getting that this isn't the ONLY way to get validation done. It's nice to be able to quicly get things done for simple projects, and extend as necessary for more complicated stuff. I do think this method does need to be fleshed out some more, but it's a great start.

  • @BlueCoder You can already do that with validation attributes at the class and property level on a metadata class. Check out the MetadataTypeAttribute class in the data annotations namespace. Just make sure the properties match up.

  • Thanks for the hint. I'll check that...

  • @Elliot

    As a Ruby on Rails developer, I would disagree. The place for validation resides in the model. Most of the time you want the same validation rules in place, regardless of the functionality of the application. An example of this would be requiring email addresses to have an @ symbol. Email address business rules don't change regardless of the web application, therefore, the validation should be a part of the model itself. This also simplifies code for business rules. Validation is reserved for the model, view layout for the view, and the controller supports the view by querying the model. Validation logic no longer has to be duplicated between the model and the controller, and developers can focus on actually getting things done and keeping things DRY.

  • How would one go about leveraging DataAnnotations validation outside of the MVC context, a.k.a. manually? Is there a validator to call for basic validation?

  • Its good and great that we can put our business logic at one place in Model regarding the Validations. However, I feel that there is tight coupling between view and Model by giving the Field names to be highlighted.

  • Wouldn't your Validate method fail to compile as not all execution paths return a result?
    What would be returned if all validation checks passed? I tried a null but that causes a null exception. Can't be a new IEnumerable, that can't be created. I guess a new List?

Comments have been disabled for this content.