Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC binding form data problem

Tags:

asp.net-mvc

I am using an object that matches all the fields in my form. I then use the default binding to populate the object in my action, like this;

public ActionResult GetDivisionData(DivisionObj FormData)

My DivisionObj initializes all it's values to string.empty in the constructor.

The problem is that when the binder populates the model from the posted form data, any data that is not posted is set to null in the object, eventhough I initialized the object to contain empty strings.

Is there a way to change this so that unposted data will be an empty string.

like image 830
John Avatar asked Apr 06 '10 13:04

John


3 Answers

This is default behavior of the DefaultModelBinder, more specifically the DataAnnotations framework. ConvertEmptyStringToNull is by default, set to true. You can create your own model binder and replace the default model binder.

public class EmptyStringModelBaseBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;

        return base.BindModel(controllerContext, bindingContext);
    }
}

Then in global...

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBaseBinder();

Though I do wish they had a static way of setting this for the default modelbinder. Maybe in v3 :)

Alternatively, You can also use the [DisplayFormat(ConvertEmptyStringToNull=false)] attribute to set this on a per-property basis.

like image 90
Jab Avatar answered Sep 19 '22 02:09

Jab


You can always use [Bind(Exclude="PropertyName1,PropertyName2,PropertyName3")] to exclude some properties from binding:

public ActionResult GetDivisionData([Bind(Exclude="PropertyName1,PropertyName2,PropertyName3")]DivisionObj FormData)

If you really have to have String.Empty in String properties, you can use this binder:

public class EmptyStringModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        if (propertyDescriptor.PropertyType == typeof(String))
            propertyDescriptor.SetValue(bindingContext.Model,propertyDescriptor.GetValue(bindingContext.Model) ?? String.Empty);
    }
}

You'll also have to run this in global.asax:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
like image 27
LukLed Avatar answered Sep 18 '22 02:09

LukLed


I can only confirm that I see the same results as you. Your options are:

One way is to exclude properties as LukLed explained. But this will lead to code duplication and you will have to do it on every controller action every time DivisionObj (or any other Model class that you wish to decorate) appears as action parameter. Soo it's a bit cumbersome...

I am currently in the process of having multiple issues with various custom properties, some that need to be instantiated in contructors, some that need value only known at runtime and others that are also special in some way.

I have determined that for me it's best to go with Custom Model Binder and do most of this stuff there.

like image 20
mare Avatar answered Sep 21 '22 02:09

mare