I\'m passing an object from client to server. Properties of the object which are represented as string.empty are being converted to null during this process. I was wondering
This is a MVC feature which binds empty strings to null
s.
This logic is controlled with the ModelMetadata.ConvertEmptyStringToNull property which is used by the DefaultModelBinder
.
You can set the ConvertEmptyStringToNull
with the DisplayFormat
attribute
public class OrderDetailsModel
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Comment { get; set; }
//...
}
However if you don't want to annotate all the properties you can create a custom model binder where you set it to false:
public class EmptyStringModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
Binders = new ModelBinderDictionary() { DefaultBinder = this };
return base.BindModel(controllerContext, bindingContext);
}
}
And you can use the ModelBinderAttribute in your action:
public ActionResult SaveOrderDetails([ModelBinder(typeof(EmptyStringModelBinder))]
OrderDetailsModel orderDetailsModel)
{
}
Or you can set it as the Default ModelBinder globally in your Global.asax:
ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
You can read more about this feature here.
The problem is that AutoMapper turns nullables unto null when string is empty. This other question was answered with something that I believe suffices your needs: Automapper null string to empty
The accepted answer did not work for me using MVC4. However, the following workaround does and I thought it would help others.
public class CustomModelBinder : DefaultModelBinder
{
public bool ConvertEmptyStringToNull { get; set; }
public CustomModelBinder ()
{
}
public CustomModelBinder (bool convertEmptyStringToNull)
{
this.ConvertEmptyStringToNull = convertEmptyStringToNull;
}
protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// this little bit is required to override the ConvertEmptyStringToNull functionality that we do not want!
foreach (string propertyKey in bindingContext.PropertyMetadata.Keys)
{
if(bindingContext.PropertyMetadata[propertyKey] != null)
bindingContext.PropertyMetadata[propertyKey].ConvertEmptyStringToNull = this.ConvertEmptyStringToNull;
}
return base.OnModelUpdating(controllerContext, bindingContext);
}
}
This will fix the issue under MVC4+. It would seem that bindingContext.ModelMetadata.ConvertEmptyStringToNull is completely ignored, and this is because the setting exists in the PropertyMetadata object for each property being bound. PropertyMetadata is recreated in BindProperty() so if you set it before that method call it will get overwritten unless it exists as an attribute on the property of your object being bound (such as [DisplayFormat(ConvertEmptyStringToNull=false)]). No one wants to do this on every property as that's silly.
Instead of creating a ModelBinder which modifies the ModelMetadata as some answers suggested, a cleaner alternative is to provide a custom ModelMetadataProvider.
public class EmptyStringDataAnnotationsModelMetadataProvider : System.Web.Mvc.DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
modelMetadata.ConvertEmptyStringToNull = false;
return modelMetadata;
}
}
Then in Application_Start()
ModelMetadataProviders.Current = new EmptyStringDataAnnotationsModelMetadataProvider();
When posting data with $.ajax
, null
is not a possible value for a property of the data
option. If you look at the request with an http debugger, you will see that it's converted to an empty string.
So I guess that your MVC controller is applying the opposite conversion.
What I do in an ajax application to workaroung this issue is that I don't use the data
option of $.ajax()
, but I serialize everything in JSON and put it into a single field "data" of the data option. Like that you don't have problems with null values. Of course, you have to deserialize on the server side.