I’m working with ASP.NET MVC
and have a problem with the value sent from Ajax
to my controller.
Let’s say I have SampleViewModel
like this:
public class SampleViewModel
{
private string _firstName = string.Empty;
public SampleViewModel()
{
_firstName = string.Empty;
}
public string FirstName
{
get { return _firstName; }
set { _firstName = value ?? string.Empty; }
}
public string LastName { get; set; }
public string FullName { get; set; }
}
Controller
[HttpPost]
public JsonResult ActionSubmit(SampleViewModel model)
{
var result = "";
if(model.FirstName == null)
result += "nFirstName is null";
if(model.LastName == null)
result += "nLastName is null";
return Json(result);
}
Ajax
$('.submit').click(function() {
$.ajax({
url: '@Url.RouteUrl(new{ action="ActionSubmit", controller="Home"})',
data: JSON.stringify({ FirstName: '', LastName: '', FullName: 'Phong_Nguyen' }),
// Even though I use { FirstName: '', LastName: '', FullName: 'Phong_Nguyen' } without JSON.stringify
type: 'POST',
dataType: 'json',
contentType: "application/json; charset=utf-8",
success: function(resp) {
alert(resp);
}});
});
As you can see, I send empty value but on the controller’s end, I get null (the response value always “LastName is null”):
Question
-
Why is it that when in Ajax I am sending
empty
, I getnull
value in my controller? -
Is there a better way and more elegant to resolve my problem like below?
public string FirstName
{
get { return _firstName; }
set { _firstName = value ?? string.Empty; }
}
4
Answers
I decided summary from @Rahul Sharma's and @rhytonix's answers along with giving you examples and more detailed explanations.
This is simply because
MVC 2.0
defaults to initializing strings to null. To be more precise, if anempty
string means has no value, So .NET sets the default value of its. And the default string (belonging to reference type) isnull
.More details in Model String Property Binding Breaking Change
There are some ways to bind String property as
string.Empty
instead ofnull
1. From C# 6, You can use DefaultValueAttribute to have auto-property an initial value like below
Basically, This way is the same as the OP's solution mentioned in the post, Just more elegant.
2. Custom default implementation of
IModelBinder
by inheriting fromDefaultModelBinder
and changing theConvertEmptyStringToNull
value to false on the internalModelMetaData
object.Then in
Application_Start()
method ofGlobal.asax.cs
you need to do like below to complete3. Use DisplayFormatAttribute.ConvertEmptyStringToNull Property like below
Simply because in ModelMetadata
string
is a reference type, and its default value isnull
. TheModelBinder
sets the properties to their default value if no value is provided in the request.You can annotate the property with
[DisplayFormat(ConvertEmptyStringToNull = false)]
, so the empty string value is preserved.You can write a custom
ModelBinder
that setsConvertEmptyStringToNull
tofalse
, and apply it globally.This particular change has been documented here and it is one of the breaking changes from
MVC 1.0
. This logic of binding empty string tonull
s is controlled with theModelMetadata.ConvertEmptyStringToNull
property which is used by theDefaultModelBinder
.Now if you do not want to annotate all your properties, you can create a custom model binder:
And set it in your
Global.asax
:Or in your specific action:
Why was this done?
This was done because the default value of a
string
isnull
and becausestring
is areference type
and the default value for all reference types isnull
. Hence, this change of the framework might be reasonable. But on the other hand, we should try to avoid null values at all, therefore we need to write a custom model binder to avoid such cases.There is a question on why the default value of the string type null instead of an empty string?. You can go over this to understand more about why this change was done.
As per @Anton: In
c# 8.0
you can turn on null checks to avoidNullReferenceException
and set to reference types default values instead ofnull
When you declare a string variable in C#, its value is
null
until assigned a value.When data is sent via a form submission, any fields where no information was entered are sent as empty strings. The best analogue to no information provided is
null
, so it sets those values tonull
(or more likely, doesn’t set a value at all).MVC cannot distinguish between an empty string because no information was provided and an empty string because that was the value assigned in JavaScript before being transmitted. It just knows one of the fields has no information, so that value should be
null
.