web-dev-qa-db-fra.com

Pourquoi le classeur de modèle ASP.Net MVC lie-t-il un tableau JSON vide à null?

Voici ma classe de modèle:

public class MyModel
{
    public Employees[] MyEmpls{get;set;}
    public int Id{get;set;}
    public OrgName{get;set;}
}

Passer l'objet de structure JSON ci-dessous avec MyEmpls as empty array au contrôleur MVC.

["Id":12, "MyEmpls":[], "OrgName":"Kekran Mcran"]

Manette

[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
  //model.MyEmpls is null here
}

Je m'attends à mode.MyEmpls pour être un tableau c # vide, pas un null. Un classeur de modèle personnalisé est-il nécessaire pour obtenir un tableau vide?

34
Billa

Je pense que certaines des autres réponses ont manqué le sens de la question: pourquoi le classeur de modèle MVC par défaut lie-t-il un tableau Json vide à null au lieu d'un tableau C # vide?

Eh bien, je ne peux pas vous dire pourquoi ils ont fait ça, mais je peux vous montrer où cela se produit. La source de MVC peut être trouvée sur CodePlex ici: http://aspnetwebstack.codeplex.com/SourceControl/latest . Le fichier que vous recherchez est ValueProviderResult.cs où vous pouvez voir:

    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);
    }
}

La partie intéressante est le "cas 3":

else
{
    // case 3(a): source is empty array, so can't perform conversion
    return null;
}

Vous pouvez contourner ce problème en initialisant votre tableau sur le modèle dans son constructeur. Dans ma lecture rapide de la source, je ne peux pas vous dire pourquoi ils ne peuvent pas retourner un tableau vide ou pourquoi ils décident de ne pas le faire, mais cela devrait rendre la lecture intéressante.

32
Richiban

Vous obtenez une valeur nulle car il s'agit de la valeur par défaut pour un type de référence en C #. Pour obtenir un tableau vide, vous devrez initialiser le tableau dans votre modèle à l'aide d'un constructeur. Cependant, comme vous devrez définir la taille du tableau lors de son initialisation, il peut être préférable d'utiliser un autre type de collection tel qu'un 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>();
    }
}

Vous obtiendrez alors une liste vide lorsqu'un tableau vide est passé depuis le json.

Si vous devez vraiment utiliser un tableau, il suffit de l'initialiser avec une taille:

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*/];
    }
}
30
James

Si vous obtenez model.MyEmpls comme null, vous pouvez créer une condition côté serveur pour arrêter de déclencher une exception comme:

if(model.MyEmpls !=null){
...
}

Et vous obtenez la valeur null car votre MyEmpls est un tableau de classe personnalisé et vous envoyez simplement [].

J'espère que cela vous aidera.

0
Mehmood

vous pouvez définir un setter qui vérifie si la valeur est nulle

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;}
}
0
hmd.ai

Essayez de créer un classeur de modèle comme ci-dessous

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
        }
    }
}

Dans Application_Start, enregistrez le classeur de modèle

ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder())

Et modifiez le contrôleur comme

[HttpPost]
public ActionResult SaveOrg([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
  //model.MyEmpls is null here
}
0
HCJ
[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 désérialise correctement les tableaux vides. Vous pouvez donc ignorer le modèle transmis et le reconstruire à partir du flux de demande d'entrée. Probablement pas de la bonne façon, mais si vous n'avez besoin de le faire qu'une fois, cela économise des efforts. Vous devrez référencer System.Web.Extensions.

0
Julian Mann