问题
Here is my model class:
public class MyModel
{
public Employees[] MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
}
Passing the below JSON structure object with MyEmpls as empty array
to MVC controller.
["Id":12, "MyEmpls":[], "OrgName":"Kekran Mcran"]
Controller
[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
//model.MyEmpls is null here
}
I am expecting mode.MyEmpls
to be an empty c# array, not a null. Is a custom model binder necessary to achieve an empty array?
回答1:
I think that some of the other answers have missed the meaning of the question: why does the default MVC model binder bind an empty Json array to null instead of an empty C# array?
Well, I can't tell you why they did that, but I can show you where it happens. The source for MVC can be found on CodePlex here: http://aspnetwebstack.codeplex.com/SourceControl/latest. The file you're looking for is ValueProviderResult.cs where you can see:
private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType)
{
if (value == null || destinationType.IsInstanceOfType(value))
{
return value;
}
// array conversion results in four cases, as below
Array valueAsArray = value as Array;
if (destinationType.IsArray)
{
Type destinationElementType = destinationType.GetElementType();
if (valueAsArray != null)
{
// case 1: both destination + source type are arrays, so convert each element
IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
for (int i = 0; i < valueAsArray.Length; i++)
{
converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType);
}
return converted;
}
else
{
// case 2: destination type is array but source is single element, so wrap element in array + convert
object element = ConvertSimpleType(culture, value, destinationElementType);
IList converted = Array.CreateInstance(destinationElementType, 1);
converted[0] = element;
return converted;
}
}
else if (valueAsArray != null)
{
// case 3: destination type is single element but source is array, so extract first element + convert
if (valueAsArray.Length > 0)
{
value = valueAsArray.GetValue(0);
return ConvertSimpleType(culture, value, destinationType);
}
else
{
// case 3(a): source is empty array, so can't perform conversion
return null;
}
}
// case 4: both destination + source type are single elements, so convert
return ConvertSimpleType(culture, value, destinationType);
}
}
The interesting part is "case 3":
else
{
// case 3(a): source is empty array, so can't perform conversion
return null;
}
You can sidestep this issue by initialising your array on the model in its constructor. In my quick reading of the source I can't tell you why they can't return an empty array or why they decide not to, but it should make for interesting reading.
回答2:
You're getting a null value as this is the default value for a reference type in C#. In order to get an empty array you will need to initialise the array in your model using a constructor. However as you will need to define the size of the array when it's initialized it might be better using another type of collection such as a List
:
public class MyModel
{
public List<Employees> MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
public MyModel()
{
MyEmpls = new List<Employees>();
}
}
You will then get an empty list when an empty array is passed from the json.
If you really have to use an array just initialise it with a size:
public class MyModel
{
public Employees[] MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
public MyModel()
{
MyEmpls = new Employees[//enter size of array in here];
}
}
回答3:
If you are getting model.MyEmpls as null then you can create a condition on server side to stop raising exception like:
if(model.MyEmpls !=null){
...
}
And you are getting it null because your MyEmpls is a Custom Class array and you are sending just [].
Hope this helps you.
回答4:
Try to create a model binder as in below
public class MyModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
try
{
var request = controllerContext.HttpContext.Request;
return new MyModel
{
MyEmpls = request[] ?? new Employees[0],
Id = request["Id"] ?? "",
OrgName = request["OrgName"] ?? ""
};
}
catch
{
//do required exception handling
}
}
}
In Application_Start register the model binder
ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder())
And modify the controller as
[HttpPost]
public ActionResult SaveOrg([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
//model.MyEmpls is null here
}
回答5:
[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
var serializer = new JavaScriptSerializer();
var stream = System.Web.HttpContext.Current.Request.InputStream;
var reader = new StreamReader(stream);
stream.Position = 0;
var json = reader.ReadToEnd();
model= serializer.Deserialize<MyModel>(json);
//model.MyEmpls is [] here
}
JavaScriptSerializer deserializes empty arrays properly. So you can ignore the passed-in model and rebuild it from the input request stream. Probably not the correct way, but if you only need to do it once it saves some effort. You'll need to reference System.Web.Extensions.
回答6:
you can define a setter that checks if the value is null
public class MyModel
{
private _myEmpls{get;set;}
public Employees[] MyEmpls{
get{return _myEmpls;}
set{_myEmpls=(value==null?new List<Employees>():value);}
}
public int Id{get;set;}
public OrgName{get;set;}
}
回答7:
The default behavior of C# array initialization is to null not empty array
So if you send an empty array you will not initiate an empty array, but you will trigger the default initialization to null
See the following link http://www.dotnetperls.com/null-array
来源:https://stackoverflow.com/questions/23108272/why-does-the-asp-net-mvc-model-binder-bind-an-empty-json-array-to-null