Friday, February 04, 2011

ASP.NET MVC - Excluding Model Validation Rules

While building a simple MVC app, I came across the problem where I am using one model in several views. The model includes DataAnnotation attribute rules for validation. However, some rules don't apply to certain views. It seems this is a common problem, without a well established solution. After searching a bit, I found this post by Andrew West that nicely summarizes the problem and several solutions. His last suggestion is to remove the items from ModelState that you don't want to participate in the validation. I agreed that this was the simplest and least dependent solution and pursued it.

What I finally came up with was a simple extension method for the ModelStateDictionary that looks like this:
public static void CleanAllBut(
this ModelStateDictionary modelState,
params string[] includes)
{
modelState
.Where(x => !includes
.Any(i => String.Compare(i, x.Key, true) == 0))
.ToList()
.ForEach(k => modelState.Remove(k));
}

The method removes everything in the model state dictionary that doesn't match one of the "includes" strings. Being a params argument to the method, the call to it becomes very clean and readable (unlike the extension method itself). So I added this call to my controller method:
ModelState.CleanAllBut("username", "password");

While we can use the existing ModelStateDictionary.Remove() method for when we only want to explicitly remove one item, for multi-key removal we could make a similar extension method that takes a list of keys to remove.

7 comments:

wow0609 said...

Very elegant solution.
One question... Where do you put something like this in your MVC project? This is a specific extension method, probably in a file by itself. Do you have a specific structure (directory / folder) you use for this? Do you have a generic /common folder or something?
Thanks!

Peter Lanoie said...

@wow0609 - I wrote another post as a more comprehensive answer to your question.

Where I put my extension code

Mark said...
This comment has been removed by the author.
Mark said...

This is great, but is it also possible to remove certain attributes from the validation model. eg. If i have create & edit views and one model, and say on an Property of Name i want to validate against Db for uniqueness, but its not the key, can i have a the controller remove a ValidationAttribute during edit, but not during Create? Hope this makes some sense

Peter Lanoie said...

@Mark - You can use the built-in members of the ModelStateDictionary to remove single elements. "While we can use the existing ModelStateDictionary.Remove() method for when we only want to explicitly remove one item...". In your controller methods, you can remove the validation state items that don't apply for the relevant action.

Junaid Ahmed said...

wow0609 add the following class anywhere

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace System.Web.Mvc
{
public static class Extensions
{
public static void CleanAllBut(this ModelStateDictionary modelState, params string[] includes)
{
modelState.Where(x => includes.Any(i => String.Compare(i, x.Key, true) == 0))
.ToList()
.ForEach(k => modelState.Remove(k));
}
}
}



Read More
http://msdn.microsoft.com/en-us/library/bb383977.aspx

http://stackoverflow.com/questions/249222/can-i-add-extension-methods-to-an-existing-static-class

Fernando Bertin said...

I know I'm really late here, but finally i found an elegant solution for what I need. The only problem is, I'm working in a project where I have to use VB.net :/ and I'm having difficulty translating that code. Could someone help me to write this in vb.net, please? I really apreciate any help.