Mvc: Html.ValidationSummary(ValidationSummary.All) throws NullReferenceException

Created on 8 Jul 2016  路  18Comments  路  Source: aspnet/Mvc

This started after I've updated my project from RC1 to ASP.NET Core 1.0. The tag helper also throws this exception.
If instead you specify ValidationSummary.ModelOnly, there's no exception. But also no property-level errors, which is bad.
Workarond: traverse ViewData.ModelState.Values manually and generate appropriate html markup.

Stack trace:

System.NullReferenceException.
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidationHelpers.Visit(ModelStateDictionary dictionary, ModelStateEntry modelStateEntry, ModelMetadata metadata, List1 orderedModelStateEntries)
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidationHelpers.Visit(ModelStateDictionary dictionary, ModelStateEntry modelStateEntry, ModelMetadata metadata, List1 orderedModelStateEntries)
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidationHelpers.GetModelStateList(ViewDataDictionary viewData, Boolean excludePropertyErrors)
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGenerator.GenerateValidationSummary(ViewContext viewContext, Boolean excludePropertyErrors, String message, String headerTag, Object htmlAttributes)
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.GenerateValidationSummary(Boolean excludePropertyErrors, String message, Object htmlAttributes, String tag)
   胁 Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.ValidationSummary(Boolean excludePropertyErrors, String message, Object htmlAttributes, String tag)
   胁 Microsoft.AspNetCore.Mvc.Rendering.HtmlHelperValidationExtensions.ValidationSummary(IHtmlHelper htmlHelper)
3 - Done bug

Most helpful comment

@prophetal
I use the following workaround in my production code for now:

<form class="form-horizontal" asp-antiforgery="true">
    <fieldset>
        // All of this instead of @Html.ValidationSummary(false) due to a bug in ASP.NET Core 1.0
        @if ([email protected])
        {
            var errors = ViewData.ModelState.Values.Select(item => item.Errors.FirstOrDefault()?.ErrorMessage).Where(item => item != null);
            <div class="alert alert-danger">
                <span>@Localizer["There are problems with your input:"]</span>
                <ul>
                    @foreach (var error in errors)
                    {
                        <li>@error</li>
                    }
                </ul>
            </div>
        }

        // Some actual fields. Don't forget validation messages for fields if you need them (@Html.ValidationMessage)
    </fieldset>
</form>

Maybe it's not suitable for you if you have many views. I have it in my only "Generic.cshtml" editor that's used to edit any entity in the application, so this workaround is in a single place. You can write an extension method that you will then call like @Html.ValidationSummaryFix(false). But I suppose its insides will be a little messy.
And no, I didn't get what genusP was going for.

All 18 comments

We definitely test w/ ValidationSummary.All. This issue is likely specific to your model types. Please share those class definitions.

@dougbu No, nothing specific to a model, this fails on all my models. I feed EF6 objects directly to the views, by the way. Such is life.

Example (simplified, but verified):

``` c#
[Table("Currency", Schema = "dic")]
public class Currency
{
[Timestamp]
[Column(Order = int.MaxValue)]
public byte[] Ts { get; set; }

[Key]
[StringLength(3, MinimumLength = 3)]
[Column(TypeName = "nchar")]
public string Id { get; set; }

[StringLength(3)]
public string CurrencySymbol { get; set; }

}

public class CurrencyController
{
[ValidateAntiForgeryToken]
[HttpPost]
[ActionName("Edit")]
public IActionResult ApplyEdit(Currency model)
{
ModelState.AddModelError(nameof(model.CurrencySymbol), "Lame!");
return View(model);
}
}


Currency/Edit.cshtml (simplified, I posted values to it via refreshing browser which remembers 'real' post values)

``` cshtml
@model object
@Html.ValidationSummary();

Debugger says that Model is of type "Currency", so is ModelMetadata.ModelType == typeof(Currency), so everything seems legit. And my other 100 tons of code that depend heavily on ModelMetadata continue to work perfectly.

Looks like a straightforward bug in ValidationHelpers.Visit(). That method (specifically this block) doesn't handle cases where a collection property is not bound. Just needs a null check on modelStateEntry.Children.

Reassigning to _do_ the straightforward fix.

@dougbu is there a workaround for this in 1.0.0 (besides the option @rsheptolut suggested)?

is there a workaround for this in 1.0.0

Since the problematic property is get-only outside ModelStateDictionary, @rsheptolut's workaround is about it. With respect to that workaround, probably best to create a DefaultHtmlGenerator subclass that overrides GenerateValidationSummary(). More generally, stick with ModelOnly and use validation message helpers to ensure property-level issues are visible.

e5cb6f9

Workarond for 1.0.0

c# private void HackModelState(string key) { var modelStateEntry = ModelState[key]; var nodeType = modelStateEntry.GetType(); nodeType.GetMethod("GetNode").Invoke(modelStateEntry, new object[] { new Microsoft.Extensions.Primitives.StringSegment("--!!f-a-k-e"), true }); ((System.Collections.IList)nodeType.GetProperty("ChildNodes").GetValue(modelStateEntry)).Clear(); }

Would it be possible to add it to 1.0.1?
It can't break anything, having to wait several months for it would be a shame.

can someone give me a hint how to implement the Workarond from genusP for 1.0.0 ?

@prophetal
I use the following workaround in my production code for now:

<form class="form-horizontal" asp-antiforgery="true">
    <fieldset>
        // All of this instead of @Html.ValidationSummary(false) due to a bug in ASP.NET Core 1.0
        @if ([email protected])
        {
            var errors = ViewData.ModelState.Values.Select(item => item.Errors.FirstOrDefault()?.ErrorMessage).Where(item => item != null);
            <div class="alert alert-danger">
                <span>@Localizer["There are problems with your input:"]</span>
                <ul>
                    @foreach (var error in errors)
                    {
                        <li>@error</li>
                    }
                </ul>
            </div>
        }

        // Some actual fields. Don't forget validation messages for fields if you need them (@Html.ValidationMessage)
    </fieldset>
</form>

Maybe it's not suitable for you if you have many views. I have it in my only "Generic.cshtml" editor that's used to edit any entity in the application, so this workaround is in a single place. You can write an extension method that you will then call like @Html.ValidationSummaryFix(false). But I suppose its insides will be a little messy.
And no, I didn't get what genusP was going for.

Thanks rsheptolut that workaround will help me alot!

I had a multi file upload field that throws the NullReferenceException.
I think there are a lot of people that will need this workaround before the 1.0.1 release.

public ICollection <IFormFile> AttachmentFiles { get; set; }

It seems like for some reason, it will be released only in 1.1.0

need call HackModelState for each enumerable property

c# public class ClOrderViewModel { ... public AttachmentViewModel[] Attachments { get; set; } ... } class OrdersController:Controller { ... [HttpPost] public IActionResult Index(int? id, ClOrderViewModel vm, [FromServices] IClOrderRepository repository) { if (ModelState.IsValid) { if (id.HasValue) repository.UpdateOrder(id.Value, vm); else id = repository.InserOrder(vm); return RedirectToAction(nameof(Index), new { id = id.Value }); } HackModelState(nameof(vm.Attachments)); return View("ClOrder", vm); } ... }

@danroth27 @Eilon should we include this in 1.0.2? @gdoron suggested that above (https://github.com/aspnet/Mvc/issues/4989#issuecomment-242229755) and in aspnet/EntityFramework#6760.

@dougbu you mean like https://github.com/aspnet/Mvc/issues/5157? 馃槃

@Eilon hmm, yeah. Fairly similar. 馃槇

Ported to rel/1.0.2 in f8f9bbc

Was this page helpful?
0 / 5 - 0 ratings