Validation Summary for Lists using Data Annotations with MVC
- by David Liddle
I currently use a custom method to display validation error messages for lists but would like to replace this system for using with Data Annotations.
In summary on validation of my form, I want to display "*" next to each input that is incorrect and also provide a Validation Summary at the bottom that relates each error message to the particular item in the list.
e.g. say if validation failed on the 2nd list item on a Name input box and the 4th item on an Address input box the validation summary would display
[2] Name is invalid
[4] Address is invalid
Please ignore if there are mistakes in the code below. I'm just writing this as an example.
The code below shows how I was able to do it using my custom version of adding model errors but was wondering how to do this using Data Annotations?
//Domain Object
public class MyObject
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public bool IsValid
    {
        get { return (GetRuleViolations().Count() == 0); }
    }
    public void IEnumerable<RuleViolation> GetRuleViolations()
    {
        if (String.IsNullOrEmpty(Name))
            yield return new RuleViolation(ID, "Name", "Name is invalid");
        if (String.IsNullOrEmpty(Address))
            yield return new RuleViolation(ID, "Address", "Address is invalid");
        yield break;
    }
}
//Rule Violation
public class RuleViolation
{
    public int ID { get; private set; }
    public string PropertyName { get; private set; }
    public string ErrorMessage { get; private set; }
}
//View
<% for (int i = 0; i < 5; i++) { %>
    <p>
        <%= Html.Hidden("myObjects[" + i + "].ID", i) %>
        Name: <%= Html.TextBox("myObjects[" + i + "].Name") %> <br />
        <%= Html.ValidationMessage("myObjects[" + i + "].Name", "*")<br />
        Address: <%= Html.TextBox("myObjects[" + i + "].Address") %> 
        <%= Html.ValidationMessage("myObjects[" + i + "].Address", "*")<br />
    </p>
<% } %>
<p><%= Html.ValidationSummary() %>
//Controller
public ActionResult MyAction(IList<MyObject> myObjects)
{
    foreach (MyObject o in myObjects)
    {
        if (!o.IsValid)
            ModelState.AddModelErrors(o.GetRuleViolations(), o.GetType().Name);
    }
    if (!Model.IsValid)
    {
        return View();
    }
}
public static class ModelStateExtensions
{
    public static void AddModelError(this ModelStateDictionary modelState, 
        RuleViolation issue, string name)
    {
        string key = String.Format("{0}[{1}].{2}", name, issue.ID, issue.PropertyName);
        string error = String.Format("[{0}] {1}", (issue.ID + 1), issue.ErrorMessage);
        //above line determines the [ID] ErrorMessage to be 
        //displayed in the Validation Summary
        modelState.AddModelError(key, error);
    }