Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override ASP.Net MVC's default model binder so that an empty value bound to a non-nullable value type won't trigger a model validation error

Something I've found quite frustrating in ASP.Net MVC is that the default model binder implicitly applies the Required annotation when binding an empty (string or null) value to a non-nullable value type instead of simply leaving the target with its default value, or at least providing an option to allow that to be the default behaviour.

Given a scenario where it's inconvenient to change the target property type on the model to a nullable value, what's the shortest amount of code I can employ to allow the default model binder to simply skip its attempt to bind an empty value to a non-nullable value type? I'm assuming I'll need to subclass DefaultModelBinder, but I'm not sure what I need to override to achieve the desired behaviour.

example:

<input type="text" name="MyField"/>

Submit without a value:

public ActionResult MyAction(MyModel model)
{
    // do stuff
}

public class MyModel
{
    public int MyField { get; set; }
}

The property MyField should be allowed to retain its default value of 0 seeing as an empty value was posted from the form.

Assume that I can't simply change the property type a Nullable<int>.

like image 818
Nathan Ridley Avatar asked Oct 03 '12 10:10

Nathan Ridley


People also ask

What is the use of ModelState IsValid in MVC?

ModelState. IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process.


1 Answers

How about something like this? (Disclaimer: Not tested to any degree of confidence)

public class NonRequiredModelBinder : DefaultModelBinder
{
    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        var result = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        if (result == null && propertyDescriptor.PropertyType.IsValueType)
            return Activator.CreateInstance(propertyDescriptor.PropertyType);

        return result;
    }
}

The idea -- in theory -- is to determine what value the DefaultModelBinder assigned to the property, check if it was a null value and then assign it to the default value of the ValueType that is being bound.

This should prevent the binder from adding ModelState errors, and still would not affect the validation of other attributes such as [Range]

I would recommend taking this a step further and creating your own attribute (i.e., NonRequiredAttribute). Then in your custom model binder you could check to see if the property has the new NonRequired attribute, and execute this custom code only in the case that it does.

like image 188
NKeddie Avatar answered Oct 06 '22 20:10

NKeddie