1

I have a scenario where 1 of 10 fields needs to be completed. When I add an error to each of the 10 properties, this results in the same error message appearing in the validation summary 10 times.

I have looked at this ValidationSummary displays duplicate messages

public static MvcHtmlString UniqueValidationSummary(this HtmlHelper html, bool excludePropertyErrors)
    {
        // do some filtering on html.ViewData.ModelState 
        return System.Web.Mvc.Html.ValidationExtensions.ValidationSummary(html, excludePropertyErrors);
    }

But I am not sure how to actually get it working. When the extension function is run on page load html.ViewData.ModelState is valid and has no messages.

How can I strip out any duplicate error messages via this extension?

Community
  • 1
  • 1
Stewart Alan
  • 1,521
  • 5
  • 23
  • 45
  • Why not use `@Html.ValidationMessageFor()` so each error can be associated with the property? –  Jan 17 '15 at 01:45

2 Answers2

2
  1. You have to write helper method that the following code.

    public static IHtmlString UniqueValidationSummary(ModelStateDictionary ms)
    {
        var resultHtml = new StringBuilder();
        resultHtml.Append("<div class=\"validation-summary-errors text-danger\" data-valmsg-summary=\"true\">");
        resultHtml.Append("<ul>");
    
        var isError = false; 
        var knownValues = new HashSet<string>();
        foreach (var key in ms.Keys)
        {
            foreach (var e in ms[key].Errors)
            {
                isError = true;
                if (!knownValues.Contains(e.ErrorMessage))
                {
                    resultHtml.Append("<li>" + e.ErrorMessage + "</li>");
                    knownValues.Add(e.ErrorMessage);
                }
            }
        }
    
        if (!isError) return null;
    
        resultHtml.Append("</ul>");
        resultHtml.Append("</div>");
    
        return new HtmlString(resultHtml.ToString());
    }
    
  2. And then, you can use helper method from view(.cshtml).

        @MyHelper.UniqueValidationSummary(ViewData.ModelState);
    
Hiroki
  • 21
  • 2
1

I wouldn't usually recommend doing this but in some cases it might be needed. I recently ran into a similar problem and needed to do the same.

Instead of trying to parse the ModelState in razor view, i did it in the controller, before returning the view. Here is the extension i used:

(Please Note that this hasnt been extensively tested, but seems to be working - i just wrote it)

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

namespace WebApplication.Common
{
    public static class  ModelStateExtension
    {
        public static void RemoveDuplicateErrorMessages(this ModelStateDictionary modelStateDictionary)
        {
            //Stores the error messages we have seen
            var knownValues = new HashSet<string>(); 

            //Create a copy of the modelstatedictionary so we can modify the original.
            var modelStateDictionaryCopy = modelStateDictionary.ToDictionary(
                element => element.Key, 
                element => element.Value); 

            foreach (var modelState in modelStateDictionaryCopy)
            {
                var modelErrorCollection = modelState.Value.Errors;
                for(var i = 0 ; i < modelErrorCollection.Count ; i++)
                {
                    //Check if we have seen the error message before by trying to add it to the HashSet
                    if (!knownValues.Add(modelErrorCollection[i].ErrorMessage)) 
                    {
                        modelStateDictionary[modelState.Key].Errors.RemoveAt(i); 
                    }    
                }
            }
        }
    }
}

You simple need to call the extension on your ModelState before returning the view:

using WebApplication.Common;    


if (!ModelState.IsValid)
{
    //viewModel code omitted

    ModelState.AddModelError("0", "Server Side Validation failed");
    ModelState.RemoveDuplicateErrorMessages();

    return View(viewModel);
}
Matthew Marlin
  • 316
  • 3
  • 11