Question

Given the following Model,

public class A
{
    public string Name { get; set; }
}

public class B
{
    public string Address { get; set; }
    public A InstanceOfA { get; set; }
}

View,

<%= Html.TextBox("A.Name") %>

and Controller

UpdateModel<B>(b, collection.ToValueProvider());

my b instance will contain a property of A with an empty string for Name.

Is there anyway to have UpdateModel set the A property to null if no value has been entered for name?

To clarify, this is a simple case, my real world scenario contains data models with hundreds of properties of this ilk. The definition of these data models is out of my hands. Therefore I need a solution for the general case, ie don't create a property if no values have been entered.

Further clarification: i need this to work in edit scenarios aswell, i.e. an instance of b with A.Name set to "foo" is edited to set A.Name to "", i want A to be null.

Was it helpful?

Solution

I just discovered this behavior (by accident thanks to a check constraint) and I think it is a mistake. I wonder how many devs are now inadvertently saving empty strings into their db instead of null? :-)

Anyway, I'm going to explore this a bit more and see if there is a better solution.

Update:

Here is a solution:

using System.ComponentModel;
using System.Web.Mvc;

namespace CustomerWebsite.Mvc
{
  public sealed class EmptyStringToNullModelBinder : DefaultModelBinder
  {
    protected override void SetProperty(ControllerContext controllerContext,
      ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    {
      if ((value != null)
          && (value.GetType() == typeof(string)
              && ((string)value).Length == 0))
      {
        value = null;
      }

      base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
    }
  }
}

And in Application_Start put this:

ModelBinders.Binders.DefaultBinder = new EmptyStringToNullModelBinder();

OTHER TIPS

This is kinda related and I thought it could help those who somehow reached this page. I am working with ASP.NET MVC 3 and in this version when an empty text box is posted to the server it's bound as null to the model.

I had to write the opposite model binder - one that changes null strings to empty strings.

I took the above code by Andrew Peters (thanks for that!) and changed it to the following.

Things to notice:

  1. As mentioned already - this model binder does the opposite.
  2. Instead of checking the type of the value I'm checking it via the propertyDescriptor, which I think is better, because this is it's purpose - to describe the value. In my case it's essential because I can't examine a value that's null. Through the descriptor I can find all that I need.

And here's code:

public sealed class CustomModelBinder : DefaultModelBinder
{
    protected override void SetProperty(ControllerContext controllerContext,
        ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    {
        if (value == null && propertyDescriptor.PropertyType == typeof(string))
            value = string.Empty;

        base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
    }
}

I hope this helps someone.

UpdateModel initialize a property when this property is in the FormCollection. In order to prevent UpdateModel from setting some properties to empty values you can delete all items from your FormCollection where value is empty. To do this you can add an extension method to the NameValueCollection type.

It appears that ASP.NET MVC 2 Preview 1 will do this for you when doing modelbinding.

ie. convert automatically ?Foo=&Bar=cat to

model.Foo = null;
model.Bar = "cat":

I actually prefer to get an empty string over a null so I'm not sure yet if its the final design but there does seem to be a change.

Thanks for your answer, I changed it a little bit so that not all string properties are changed, and properties that are not string can be changed too. Thanks to DefaultValueAttribute.

public class DefaultValueModelBinder: DefaultModelBinder
{
  protected override void SetProperty( ControllerContext controllerContext,
                                       ModelBindingContext bindingContext,
                                       PropertyDescriptor propertyDescriptor, 
                                       object value )
  {
    if( value == null )
    {
      var defaultValue = (DefaultValueAttribute)propertyDescriptor.Attributes[ typeof(DefaultValueAttribute) ];
      if( defaultValue != null )
        value = defaultValue.Value;
    }

    base.SetProperty( controllerContext, bindingContext, propertyDescriptor, value );
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top