Chiamare UpdateModel con una raccolta di tipi di dati complessi reimpostare tutti i valori non associati?

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

Domanda

Non sono sicuro che si tratti di un bug nella classe DefaultModelBinder o cosa. Ma UpdateModel di solito non modifica alcun valore del modello tranne quelli per i quali ha trovato una corrispondenza. Dai un'occhiata a quanto segue:

[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" });
    // ...
}

Quello che succede è UpdateModel crea nuovi oggetti Person, assegna il loro nome & amp; Valorizza le proprietà dal ValueProvider e inseriscile nell'argomento List & Lt; & Gt ;, che imposta il resto delle proprietà sul loro valore iniziale predefinito (ad es. Id = 0) quindi cosa sta succedendo qui?

È stato utile?

Soluzione

UPDATE: Ho esaminato il codice sorgente mvc (in particolare DefaultModelBinder classe) ed ecco cosa ho trovato:

La classe determina che stiamo tentando di associare una raccolta, quindi chiama il metodo: UpdateCollection(...) che crea un ModelBindingContext interno che ha una proprietà null Model. Successivamente, quel contesto viene inviato al metodo BindComplexModel(...) che controlla la <=> proprietà per <=> e crea una nuova istanza del tipo di modello, se questo è il caso.

Questo è ciò che provoca il ripristino dei valori.

E così, vengono popolati solo i valori che arrivano attraverso i dati form / query string / route, il resto rimane nel suo stato inizializzato.

Sono stato in grado di apportare pochissime modifiche a <=> per risolvere questo problema.

Ecco il metodo con le mie modifiche:

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;

}

Altri suggerimenti

Rudi Breedenraed ha appena scritto un eccellente post descrivere questo problema e una soluzione molto utile. Sostituisce DefaultModelBinder e quindi quando si imbatte in una raccolta da aggiornare, aggiorna effettivamente l'elemento invece di crearne uno nuovo come il comportamento MVC predefinito. Con ciò, il comportamento di UpdateModel () e TryUpdateModel () è coerente sia con il modello radice che con tutte le raccolte.

Mi hai appena dato l'idea di scavare nel codice sorgente ASP.NET MVC 2. Ho lottato con questo per due settimane ormai. Ho scoperto che la tua soluzione non funzionerà con gli elenchi nidificati. Ho inserito un punto di interruzione nel metodo UpdateCollection e non viene mai colpito. Sembra che il livello principale del modello debba essere un elenco per poter chiamare questo metodo

Questo è in breve il modello che ho ... Ho anche un altro livello di elenchi generici, ma questo è solo un rapido esempio ..

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

Suppongo che dovrò scavare più a fondo per scoprire cosa sta succedendo.

UPDATE: UpdateCollection viene ancora chiamato in asp.net mvc 2, ma il problema con la correzione sopra è correlato a questo QUI

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top