Chamando UpdateModel com uma coleção de tipos de dados complexos repor todos os valores não-vinculadas?

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

Pergunta

Eu não tenho certeza se este é um bug na classe DefaultModelBinder ou o quê. Mas UpdateModel geralmente não alterar quaisquer valores do modelo, exceto aqueles que encontrou uma correspondência para. Dê uma olhada no seguinte:

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

O que acontece é UpdateModel cria new Pessoa objetos, atribuir suas propriedades Nome e Idade do ValueProvider e colocá-los na lista de argumentos <>, o que torna o resto das propriedades definidas para o seu padrão inicial valor (por exemplo, Id = 0) Então, o que está acontecendo aqui?

Foi útil?

Solução

UPDATE: Eu pisei através de código MVC fonte (classe particularmente DefaultModelBinder) e aqui está o que eu encontrei:

A classe determina que estamos a tentar ligar uma coleção para que ele chama o método: UpdateCollection(...) que cria um ModelBindingContext interior que tem uma propriedade null Model. Depois, nesse contexto, é enviado para o BindComplexModel(...) método que verifica a propriedade Model para null e cria um new instância do tipo de modelo, se for esse o caso.

Isso é o que faz com que os valores a ser reposto.

E assim, apenas os valores que estão vindo através do formulário / string de consulta / encaminhar os dados são preenchidos, os restos de descanso em seu estado inicializado.

Eu era capaz de fazer muito poucas mudanças para UpdateCollection(...) para corrigir esse problema.

Aqui é o método com as minhas alterações:

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;

}

Outras dicas

Rudi Breedenraed apenas escreveu um excelente pós que descreve este problema e uma solução muito útil. Ele substitui a DefaultModelBinder e, quando ele se depara com uma coleção para atualização, ele realmente atualiza o item em vez de criá-lo novo, como o comportamento padrão MVC. Com isso, UpdateModel () e TryUpdateModel () comportamento é consistente com o modelo de raiz e quaisquer cobranças.

Você só me deu uma idéia para cavar ASP.NET MVC código de 2 fonte. Eu tenho lutado com isso por duas semanas agora. Eu descobri que sua solução não irá funcionar com listas aninhadas. Eu coloquei um ponto de interrupção no método UpdateCollection, e nunca é atingido. Parece que o nível raiz do modelo precisa ser uma lista para este método a ser chamado

Este é em suma o modelo I have..I também têm mais um nível de listas genéricas, mas esta é apenas uma amostra rápida ..

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

Eu acho que isso, vou precisar de cavar mais fundo para descobrir o que está acontecendo.

UPDATE: O UpdateCollection ainda é chamado em asp.net MVC 2, mas o problema com a correção acima está relacionado a este AQUI

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top