Ever wondered just what the ModelState
object was that keeps popping up in your ASP.NET MVC controllers? Have you seen the property ModelState.IsValid
and didn't know what it did? You are not alone.
Let's break down what the ModelState
in ASP.NET MVC is, and why we use it.
What is the ModelState?
ModelState
is a property of a Controller
object, and can be accessed from those classes that inherit from System.Web.Mvc.Controller.
The ModelState
represents a collection of name and value pairs that were submitted to the server during a POST. It also contains a collection of error messages for each value submitted. Despite its name, it doesn't actually know anything about any model classes, it only has names, values, and errors.
The ModelState
has two purposes: to store the value submitted to the server, and to store the validation errors associated with those values.
But that's the boring explanation. Show me the code!
The Sample Project
Check out my (very simple) sample project hosted over on GitHub!
The Setup
Now, let's get started writing the code for this demo. First, we have the AddUserVM
view model:
public class AddUserVM
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
Next, we have a simple view at Home/Add:
@model ModelStateDemo.ViewModels.Home.AddUserVM
<h2>Add</h2>
@using(Html.BeginForm())
{
<div>
<div>
@Html.TextBoxFor(x => x.FirstName)
</div>
<div>
@Html.TextBoxFor(x => x.LastName)
</div>
<div>
@Html.TextBoxFor(x => x.EmailAddress)
</div>
<div>
<input type="submit" value="Save" />
</div>
</div>
}
Finally, we have the controller HomeController
and its actions:
...
[HttpGet]
public ActionResult Add()
{
AddUserVM model = new AddUserVM();
return View(model);
}
[HttpPost]
public ActionResult Add(AddUserVM model)
{
if(!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("Index");
}
When we submit the form to the POST action, all of the values we entered will show up in the AddUserVM
instance. But how did they get there?
The ModelStateDictionary Class
Let's look at the rendered HTML form for the Add page:
<form action="/Home/Add" method="post">
<div>
<div>
<label for="FirstName">First Name:</label>
<input id="FirstName" name="FirstName" type="text" value="">
</div>
<div>
<label for="LastName">Last Name:</label>
<input id="LastName" name="LastName" type="text" value="">
</div>
<div>
<label for="EmailAddress">Email Address:</label>
<input id="EmailAddress" name="EmailAddress" type="text" value="">
</div>
<div>
<input type="submit" value="Save">
</div>
</div>
</form>
In a POST, all values in <input>
tags are submitted to the server as key-value pairs. When MVC receives a POST, it takes all of the post parameters and adds them to a ModelStateDictionary instance. When debugging the controller POST action in Visual Studio, we can use the Locals window to investigate this dictionary:

The Values property of the ModelStateDictionary
contains instances that are of type System.Web.Mvc.ModelState. What does a ModelState
actually contain?
What's in a ModelState?
Here's what those values look like, from the same debugger session:

Each of the properties has an instance of ValueProviderResult that contains the actual values submitted to the server. MVC creates all of these instances automatically for us when we submit a POST with data, and the POST action has inputs that map to the submitted values. Essentially, MVC is wrapping the user inputs into more server-friendly classes (ModelState
and ValueProviderResult
) for easier use.
There's still two important properties that we haven't discussed, though: the ModelState.Errors
property and the ModelState.IsValid
property. They're used for the second function of ModelState
: to store the errors found in the submitted values.
Validation Errors in ModelState
Let's change our AddUserVM
view model class:
public class AddUserVM
{
[Required(ErrorMessage = "Please enter the user's first name.")]
[StringLength(50, ErrorMessage = "The First Name must be less than {1} characters.")]
[Display(Name = "First Name:")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter the user's last name.")]
[StringLength(50, ErrorMessage = "The Last Name must be less than {1} characters.")]
[Display(Name = "Last Name:")]
public string LastName { get; set; }
[EmailAddress(ErrorMessage = "The Email Address is not valid")]
[Required(ErrorMessage = "Please enter an email address.")]
[Display(Name = "Email Address:")]
public string EmailAddress { get; set; }
}
We've added validation attributes, specifically Required, StringLength, and EmailAddress. We've also set the error messages that are to be displayed if the corresponding validation errors occur.
With the above changes in place, let's modify the Home/Add.cshtml view to display the error messages if they occur:
@model ModelStateDemo.ViewModels.Home.AddUserVM
<h2>Add</h2>
@using(Html.BeginForm())
{
@Html.ValidationSummary()
<div>
<div>
@Html.LabelFor(x => x.FirstName)
@Html.TextBoxFor(x => x.FirstName)
@Html.ValidationMessageFor(x => x.FirstName)
</div>
<div>
@Html.LabelFor(x => x.LastName)
@Html.TextBoxFor(x => x.LastName)
@Html.ValidationMessageFor(x => x.LastName)
</div>
<div>
@Html.LabelFor(x => x.EmailAddress)
@Html.TextBoxFor(x => x.EmailAddress)
@Html.ValidationMessageFor(x => x.EmailAddress)
</div>
<div>
<input type="submit" value="Save" />
</div>
</div>
}
Notice the two helpers we are using now, ValidationSummary and ValidationMessageFor.
ValidationSummary
reads all errors from the model state and displays them in a bulleted list.ValidationMessageFor
displays only errors for to the property specified.
Let's see what happens when we attempt to submit an invalid POST that is missing the email address. When we get to the POST action while debugging, we have the following values in our ModelStateDictionary
instance:

Note that the ModelState
instance for the email address now has an error in the Errors
collection. When MVC creates the model state for the submitted properties, it also iterates through each property in the view model and validates the property using attributes associated to it. If any errors are found, they are added to the Errors
collection in the property's ModelState
.
Also note that ModelState.IsValid
is false
now. That's because an error exists; ModelState.IsValid
is false
if any of the properties submitted have any error messages attached to them.
What all of this means is that by setting up the validation in this manner, we allow MVC to just work the way it was designed. The ModelState
stores the submitted values, allows them to be mapped to class properties (or just as parameters to the action) and keeps a collection of error messages for each property. In simple scenarios, this is all we need, and all of it is happening behind the scenes!
Custom Validation
But what if we needed to perform more complex validation than what is provided by attributes? Say we needed to validate that the first and last names are not identical, and display a particular error message when this happens.
We can actually add errors to the model state via the AddModelError()
method on ModelStateDictionary
:
[HttpPost]
public ActionResult Add(AddUserVM model)
{
if(model.FirstName == model.LastName)
{
ModelState.AddModelError("LastName",
"The last name cannot be the same as the first name.");
}
if(!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("Index");
}
The first parameter to the AddModelError()
method is the name of the property that the error applies to. In this case, we set it to LastName. You could also set it to nothing (or a fake name) if you just want it to appear in the ValidationSummary
and not in a ValidationMessage
.
Now the error will be displayed on the page:

Summary
The ModelState
represents the submitted values and errors in said values during a POST. The validation process respects the attributes like [Required]
and [EmailAddress]
, and we can add custom errors to the validation if we so desire. ValidationSummary
and ValidationMessageFor
read directly from the ModelState
to display errors to the user.
Don't forget, I've got a very simple sample project on Github that demonstrates how the ModelState works and provides all the code and markup in this post.
Also, if this post helped you, please consider buying me a coffee. Your support funds all of my projects and helps me keep traditional ads off this site. Thanks!
Happy Coding!
Hello matthew i like your Explanation on validation and
i need validation on UserName or Unique EmailAddress
Please As soon as possible post it this kind of article
Thank you for the article! very well explained and straightforward :)
Thanks for the clear explanation, Matthew!
This is awesome....... Thanks so much for this
great article! thank you!
+1 for simplification. Thank you for writing and uploading.
Thanks, still very useful information even for ASP.Net Core!
You're very welcome Albert, thanks for reading!
Well thank you thank you! The mention of looking in Locals for what is in the ModelState helped me get a handle on why my ModelState was returning false. As we all know the authentication and Identity tables are off in a world of their own. I needed to have a whole bunch of extra stuff about the registered user and I made an accounts table and an account row everytime a user registered. Then I joined them in SQL server with a view and that's when everything went south. Turns out by looking in Locals I saw there were 3 fields I was validating that didn't exist in the SQL Server view(I know, I probably could have done a Linq join whatever, but I has some intense conditional Data Annotations stuff going that worked both client and server side and wanted to keep that.)
So now that the erroring fields are out I'm hoping to continue on with this mess called MVC. Thank you very much and be forwarned. . . . .YOU ARE BOOKMARKED in my favorites!
Very good article, thank you for the wonderful explanation.
... excellent ! I have been caught in maze of half-explanations on the web for days. Finally someone who knows what he knows. Good job Matt ...
... I am very new in MVC so I would like to ask you one question. How do I retrieve data from View, using HTTPContextBase or ModelState ...
MVC is a new tool in my belt and this post was just what I needed to solve an issue I was having with a project. Really well written, clear and concise. Thanks so much! Exception Not Found has a new subscriber.
You're very welcome, Vulcan Dog. Thanks for reading!
Nice post Jones.
However, I am just a beginner, and I am finding it difficult to implement validation in my scenario. It would be great if you can help on this,
I have a view (with 2 partial views using same model). on submit action, i want to validate only one partial view, ignoring other.
Example: I have a Shared View called "Student". And i use it as "Student1" partial view with Student model, "Student2" partial view with Student model. Both partial views in MainPage view. I have a submit action in MainPage view, where i have to validate only Student1, and ignore the validations in Student2.
Appreciate if you could help. Thanks.
Useful post!
Thanks for sharing!
Hi Matt,
I am very found of your articles they are always easy and well explained, Thanks!
You're very welcome Imran!
why to send
AddUserVM model = new AddUserVM();
in add action of HTTPGET
If you use Telerik controls and Viewbags etc what is the best way to return the model etc when the modelstate is invalid?
A very informative and well-written article - thanks for taking the time.
i dont understand why you put "x => x.LastName" i saw other code with "model=> model.LastName" for example. im a begginer i am trying to understand. sorry if my question is stupid or something. thanks.
No problem Mario. Both "x" and "model" are placeholders, representing each item in the collection. The actual name of these placeholders is irrelevant. I could call them "matts" and they would work just fine.
Awesome article. I am very new for MVC. But they way you have explained it, its awesome. After going through this article, I was trying to find out, if you have any article for MVC basic.
Really Amazing.
This is the best article I have ever seen about ModelState! Thank you!
Hi Matthew!
Recently I've been working with custom validations and I hadn't have any problems until I tried the same in a view but with an Ajax.BeginForm instead of a Html.BeginForm.
The data is not being sent to the next layer because the ModelState is not valid (that part works as expected), but in the View the ValidationMessageFor helpers are not showing anything (except for the common Data Annotations like Required, Range, etc).
Do you know why is this happening and how to solve it by chance?
Regards.
really wonderful article. I'm on Core 2.1.1 and ModelState.AddModelError("LastName", "The last name cannot be the same as the first name."); isnt working.
I had to include instance name to get it working
ModelState.AddModelError("model.LastName", "The last name cannot be the same as the first name.");
So nicely explained! Thanx!
thanks a lot for this wonderfully explication.
Thanks for this. I found this article when I was having trouble getting remote validation working the way I wanted. Using this method means the validation error message can contain detailed info about the issue, which I needed.
I learned something valuable, Thanks.
I knew of ModelState, but not that it could be so useful.
Great article! thank you!
Nice intro!
Awesome! Very well explained. Easy to understand. Thank you.
Thanks a lot @mpjones28:disqus ......well explained
Thanks for the helpful information!
This was fantastic! So simple and well explained - it propelled my understanding of MVC several months! Thank you!
You're very welcome Niklas!
If you're still learning MVC, you can check out the other installments of ASP.NET MVC Demystified here: http://exceptionnotfound.ne...
Yes, I have been looking at the other articles as well, also golden! I'm currently learning the MVC pattern in EPiServer, and they add more complexity to the equation, but your explanation of the ModelState object lead me to understanding their handling of ViewModels as well.
Excellent lesson. Thank you so much!
thank you so much man you just made my day.
Very well written and understandable. Thanks for your article!
Matthew, thank you very much for this simple and wonderful article
Thanks, instantly demystified the whole thing :)
One of the best and simplest explanations of ModelState and Field Validation. Thank you so much for this.
You're very welcome Sanket. Thanks for reading!
Thank you for the great article, i did bookmark it for it's beauty :) Thank You again realy helpful.
Amazingly helpful and crystal-clear explanation. That's exactly what I've been looking for! I sweep off my hat :)
Excellent Article. I have easily understand this. Thanks a Lot.
Thanks , before I haven't searched the error message in the all keys.
Awesome! I was looking for what is that used for and your article explains it perfectly. Thanks!!
You're very welcome Suha! Thanks for reading!
Great nice explanation
brilliant .. i love your posts ... simple and easy to understand
Great description, told me what I needed to know - and well written too I would add. Cheers
Thanks for such a wonderful article.
You're very welcome Palash, thanks for reading!
which is the best way you consider to make a validation when you return a json on a view? Do you return an object with all error messages from the ModelState and then update the ui using javascript? or you return a 400 error on the response?
Thank you!
thanks
Thanks Matthew
Thanks for the explanation and example. Its really helpful for the beginner like me. Thanks again and keep writing.
Gracias Mister ^-^
De nada. :)
Simply Thank you for explaining ModelState in understandable manner
Wonderful explanation!! Precise to the point.