L'appel de UpdateModel avec une collection de types de données complexes réinitialise-t-il toutes les valeurs non liées?

StackOverflow https://stackoverflow.com/questions/1207991

Question

Je ne sais pas s'il s'agit d'un bogue dans la classe DefaultModelBinder ou quoi. Mais UpdateModel ne modifie généralement aucune valeur du modèle, à l'exception de celles pour lesquelles il a trouvé une correspondance. Jetez un coup d'œil à ce qui suit:

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Edit(List<int> Ids)
{
    // Load list of persons from the database
    List<Person> people = GetFromDatabase(Ids);
    // shouldn't this update only the Name & Age properties of each Person object
    // in the collection and leave the rest of the properties (e.g. Id, Address)
    // with their original value (whatever they were when retrieved from the db)
    UpdateModel(people, "myPersonPrefix", new string[] { "Name", "Age" });
    // ...
}

Qu'est-ce qui se passe si UpdateModel crée des nouveaux objets Personne, attribuez leur Nom & amp; Définissez les propriétés d'âge de ValueProvider et mettez-les dans l'argument List & Lt; & Gt ;,, ce qui permet de définir le reste des propriétés sur leur valeur initiale par défaut (par exemple Id = 0). alors qu'est-ce qui se passe ici?

Était-ce utile?

La solution

MISE À JOUR: J'ai parcouru le code source mvc (en particulier la DefaultModelBinder classe) et voici ce que j'ai trouvé:

La classe détermine que nous essayons de lier une collection. Elle appelle donc la méthode: UpdateCollection(...) qui crée un ModelBindingContext interne ayant une propriété null Model. Ensuite, ce contexte est envoyé à la méthode BindComplexModel(...) qui vérifie <=> la propriété <=> et crée une instance new du type de modèle, le cas échéant.

C'est ce qui provoque la réinitialisation des valeurs.

Ainsi, seules les valeurs qui entrent dans les données de formulaire / chaîne de requête / route sont renseignées, le reste reste dans son état initialisé.

J'ai pu apporter très peu de modifications à <=> pour résoudre ce problème.

Voici la méthode avec mes modifications:

internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) {
IModelBinder elementBinder = Binders.GetBinder(elementType);

// build up a list of items from the request
List<object> modelList = new List<object>();
for (int currentIndex = 0; ; currentIndex++) {
    string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
    if (!DictionaryHelpers.DoesAnyKeyHavePrefix(bindingContext.ValueProvider, subIndexKey)) {
        // we ran out of elements to pull
        break;
    }
    // **********************************************************
    // The DefaultModelBinder shouldn't always create a new
    // instance of elementType in the collection we are updating here.
    // If an instance already exists, then we should update it, not create a new one.
    // **********************************************************
    IList containerModel = bindingContext.Model as IList;
    object elementModel = null;
    if (containerModel != null && currentIndex < containerModel.Count)
    {
        elementModel = containerModel[currentIndex];
    }
     //*****************************************************
    ModelBindingContext innerContext = new ModelBindingContext() {
        Model = elementModel, // assign the Model property
        ModelName = subIndexKey,
        ModelState = bindingContext.ModelState,
        ModelType = elementType,
        PropertyFilter = bindingContext.PropertyFilter,
        ValueProvider = bindingContext.ValueProvider
    };
    object thisElement = elementBinder.BindModel(controllerContext, innerContext);

    // we need to merge model errors up
    VerifyValueUsability(controllerContext, bindingContext.ModelState, subIndexKey, elementType, thisElement);
    modelList.Add(thisElement);
}

// if there weren't any elements at all in the request, just return
if (modelList.Count == 0) {
    return null;
}

// replace the original collection
object collection = bindingContext.Model;
CollectionHelpers.ReplaceCollection(elementType, collection, modelList);
return collection;

}

Autres conseils

Rudi Breedenraed vient d'écrire un excellent post décrivant ce problème et une solution très utile. Il remplace le DefaultModelBinder, puis lorsqu'il détecte une collection à mettre à jour, il met réellement à jour l'élément au lieu de le créer comme le comportement par défaut de MVC. Ainsi, les comportements UpdateModel () et TryUpdateModel () sont cohérents avec le modèle racine et les collections.

Vous venez de me donner une idée de creuser dans le code source ASP.NET MVC 2. Je me bats avec cela depuis deux semaines maintenant. J'ai découvert que votre solution ne fonctionnerait pas avec des listes imbriquées. J'ai mis un point d'arrêt dans la méthode UpdateCollection, et il n'est jamais touché. Il semble que le niveau racine du modèle doit être une liste pour que cette méthode soit appelée

C’est en résumé le modèle que j’ai .. j’ai également un niveau supplémentaire de listes génériques, mais il ne s’agit que d’un échantillon rapide ..

public class Borrowers
{
   public string FirstName{get;set;}
   public string LastName{get;set;}
   public List<Address> Addresses{get;set;}
}

Je suppose que, je vais devoir creuser plus profondément pour savoir ce qui se passe.

UPDATE: La UpdateCollection est toujours appelée dans asp.net mvc 2, mais le problème avec le correctif ci-dessus est lié à cette ICI

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top