14

Is there a way to remove ModelState errors during an ASP.NET MVC postback without having to write each one by hand.

Let's say we have a checkbox Billing Same As Shipping and we want to then ignore anything user wrote for ShippingAddress when it's checked - typically what you might do is this.

ModelState.Remove("Checkout.ShipppingAddress.FirstName");
ModelState.Remove("Checkout.ShipppingAddress.LastName");
ModelState.Remove("Checkout.ShipppingAddress.Address1");
ModelState.Remove("Checkout.ShipppingAddress.Address2");
...
ModelState.Remove("Checkout.ShipppingAddress.ZipCode");
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689

3 Answers3

30

Here's my solution - a RemoveFor() extension method on ModelState, modelled after MVC HTML helpers:

    public static void RemoveFor<TModel>(this ModelStateDictionary modelState, 
                                         Expression<Func<TModel, object>> expression)
    {
        string expressionText = ExpressionHelper.GetExpressionText(expression);

        foreach (var ms in modelState.ToArray())
        {
            if (ms.Key.StartsWith(expressionText + "."))
            {
                modelState.Remove(ms);
            }
        }
    }

Here's how it's used :

if (model.CheckoutModel.ShipToBillingAddress == true) 
{
    // COPY BILLING ADDRESS --> SHIPPING ADDRESS
    ShoppingCart.ShippingAddress = ShoppingCart.BillingAddress;

    // REMOVE MODELSTATE ERRORS FOR SHIPPING ADDRESS
    ModelState.RemoveFor<SinglePageStoreModel>(x => x.CheckoutModel.ShippingAddress);
}

if (ModelState.IsValid) 
{
     // should get here now provided billing address is valid
}

If anybody can see a way to improve it (or not have to specify the generic type argument) then please let me know. Or if this exists in MvcFutures under a different name I'd rather switch to that.

While I'm at it here's a helper to check if ModelState is valid for a certain 'tree'

    public static bool IsValidFor<TModel, TProperty>(this TModel model,
                                                     System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression,
                                                     ModelStateDictionary modelState)
    {
        string name = ExpressionHelper.GetExpressionText(expression);

        return modelState.IsValidField(name);
    }

Which can be used like this :

 if (model.IsValidFor(x => x.CheckoutModel.BillingAddress, ModelState))
 {
     _debugLogger.Log("Billing Address Valid", () => ShoppingCart.BillingAddress);
 }
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 2
    You can specify the model type in the expression rather than via the generic argument if you like your brackets round rather than pointy: ModelState.RemoveFor((SinglePageStoreModel x) => x.CheckoutModel.ShippingAddress); – Dan Malcolm Dec 10 '13 at 16:20
  • 1
    Worth noting that the period in the StartWith check needs to be removed when looking for properties in the object specified (the root). – Jay Zelos Feb 02 '15 at 11:26
  • @Simon_Weaver Thank you for sharing the solution. – Thomas.Benz Nov 09 '17 at 22:47
4

If this is for MVC 6, suggest using ModelBindingHelper.ClearValidationStateForModel(Type, ModelStateDictionary, IModelMetadataProvider, string).

Doug
  • 101
  • 3
  • very cool :-) for anyone curious I found the current implementation here https://github.com/aspnet/Mvc/blob/7b433820b17c9a71f6924e95c746a1339fe439d3/src/Microsoft.AspNet.Mvc.Core/ModelBindingHelper.cs – Simon_Weaver Sep 19 '15 at 21:50
  • if anyone ever sees any difference between the behavior of this method and my original answer please let me know – Simon_Weaver Sep 19 '15 at 21:50
  • @Doug Is there the similar open source ModelBidingHlper for MVC 5? I still use MVC 5 and I do not know where I find such an open source for MVC 5. Thanks for you help. – Thomas.Benz Nov 09 '17 at 22:52
  • @Thomas.Benz `ModelBindingHelper` is new with ASP.NET MVC Core. Use @Simon_Weaver's solution for ASP.NET MVC 5. – Doug Nov 11 '17 at 05:25
  • BTW source for MVC 5 is [here](https://github.com/aspnet/AspNetWebStack). – Doug Nov 11 '17 at 05:28
  • 1
    Has this been removed from .NET Core 3, or require some extra package? Visual Studio has no idea what a `ModelBindingHelper` is in my .NET Core 3 Razor Pages project. If I can find it, do I inject it to make it available? – FirstDivision Jan 22 '20 at 21:03
  • Are you trying to use `ModelBindingHelper` in a *.csphtml or *.cs file? If the first, add `@using Microsoft.AspNetCore.Mvc.ModelBinding` because that's not in _ViewImports.cshtml by default. Otherwise, add a regular C# `using` to your source file. – Doug Jan 24 '20 at 04:03
0

Asp core 2.2 razor pages. Removes a nested objects modelstate(onpost) error in a list on type Input.ClubInfo.

        for (int i = 0; i < Input.ClubInfo?.Count; i++)
        {
            ModelState.Remove("Input.ClubInfo[" + i + "].Membershipnr2");
        }
DrNice
  • 51
  • 6