¿Llamar a UpdateModel con una colección de tipos de datos complejos restablece todos los valores no vinculados?

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

Pregunta

No estoy seguro de si se trata de un error en la clase DefaultModelBinder o qué.Pero UpdateModel generalmente no cambia ningún valor del modelo excepto aquellos para los que encontró coincidencia.Eche un vistazo a lo siguiente:

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

Lo que sucede es que UpdateModel crea nuevo Objetos de persona, asigne sus propiedades Nombre y Edad desde ValueProvider y colóquelas en el argumento Lista<>, lo que hace que el resto de las propiedades se establezcan en su valor inicial predeterminado (p. ej.Id = 0) Entonces, ¿qué está pasando aquí?

¿Fue útil?

Solución

ACTUALIZAR:Revisé el código fuente de mvc (particularmente DefaultModelBinder clase) y esto es lo que encontré:

La clase determina que estamos intentando vincular una colección por lo que llama al método: UpdateCollection(...) que crea un interior ModelBindingContext eso tiene un null Model propiedad.Luego, ese contexto se envía al método. BindComplexModel(...) que comprueba el Model propiedad para null y crea un nuevo instancia del tipo de modelo si ese es el caso.

Eso es lo que hace que se restablezcan los valores.

Y así, solo se completan los valores que llegan a través del formulario/cadena de consulta/datos de ruta, el resto permanece en su estado inicializado.

Pude hacer muy pocos cambios en UpdateCollection(...) para solucionar este problema.

Aquí está el método con mis cambios:

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;

}

Otros consejos

Rudi Breedenraed acaba de escribir una excelente post describiendo este problema y una solución muy útil. Anula DefaultModelBinder y luego, cuando se trata de una colección para actualizar, en realidad actualiza el elemento en lugar de crearlo como el comportamiento MVC predeterminado. Con esto, el comportamiento de UpdateModel () y TryUpdateModel () es consistente tanto con el modelo raíz como con cualquier colección.

Me acabas de dar una idea para profundizar en el código fuente ASP.NET MVC 2. He estado luchando con esto durante dos semanas. Descubrí que su solución no funcionará con listas anidadas. Puse un punto de interrupción en el método UpdateCollection, y nunca se golpea. Parece que el nivel raíz del modelo debe ser una lista para que este método se llame

Este es, en resumen, el modelo que tengo ... También tengo un nivel más de listas genéricas, pero esto es solo una muestra rápida ...

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

Supongo que tendré que profundizar para descubrir qué está pasando.

ACTUALIZACIÓN: Todavía se llama a UpdateCollection en asp.net mvc 2, pero el problema con la corrección anterior está relacionado con esto AQUÍ

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top