La chiamata a UpdateModel con una raccolta di tipi di dati complessi reimposta tutti i valori non vincolati?

Non sono sicuro se si tratta di un bug nella class DefaultModelBinder o cosa. Ma UpdateModel di solito non cambia alcun valore del modello tranne quelli per cui ha trovato una corrispondenza. Dai un’occhiata a quanto segue:

[AcceptVerbs(HttpVerbs.Post)] public ViewResult Edit(List Ids) { // Load list of persons from the database List 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 (eg Id, Address) // with their original value (whatever they were when retrieved from the db) UpdateModel(people, "myPersonPrefix", new string[] { "Name", "Age" }); // ... } 

Quello che succede è che UpdateModel crea nuovi oggetti Person, assegna loro proprietà Name & Age da ValueProvider e li inserisce nell’argomento List , il che rende il resto delle proprietà impostato sul loro valore iniziale predefinito (es. Id = 0), quindi cosa è sta succedendo qui?

AGGIORNAMENTO: ho passato il codice sorgente di mvc (in particolare la class DefaultModelBinder ) ed ecco cosa ho trovato:

La class determina che stiamo cercando di associare una raccolta in modo che chiami il metodo: UpdateCollection(...) che crea un ModelBindingContext interno che ha una proprietà Model null . Successivamente, quel contesto viene inviato al metodo BindComplexModel(...) che controlla la proprietà Model per null e crea una nuova istanza del tipo di modello, se questo è il caso.

Questo è ciò che causa il reset dei valori.

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

Sono stato in grado di apportare pochissime modifiche a UpdateCollection(...) 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 modelList = new List(); 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; 

}

Rudi Breedenraed ha appena scritto un post eccellente che descrive questo problema e una soluzione molto utile. Sostituisce DefaultModelBinder e quindi quando incontra una raccolta da aggiornare, in realtà aggiorna l’elemento anziché crearlo come il comportamento MVC predefinito. Con questo, il comportamento UpdateModel () e TryUpdateModel () è coerente sia con il modello radice sia con eventuali raccolte.

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

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

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

Immagino che, avrò bisogno di scavare più a fondo per scoprire cosa sta succedendo.

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