Вызов UpdateModel с набором сложных типов данных сбрасывает все несвязанные значения? - PullRequest
8 голосов
/ 30 июля 2009

Я не уверен, если это ошибка в классе DefaultModelBinder или что. Но UpdateModel обычно не меняет никаких значений модели, кроме тех, для которых она нашла соответствие. Взгляните на следующее:

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

Что происходит, когда UpdateModel создает new Объекты Person, присваивает их свойства Name & Age из ValueProvider и помещает их в аргумент List <>, что делает остальные свойства установленными в их начальное значение по умолчанию (например, Id = 0) так что здесь происходит?

Ответы [ 3 ]

9 голосов
/ 30 июля 2009

UPDATE: Я прошел через исходный код mvc (особенно класс DefaultModelBinder) и вот что я нашел:

Класс определяет, что мы пытаемся связать коллекцию, поэтому он вызывает метод: UpdateCollection(...), который создает внутреннее ModelBindingContext со свойством null Model. Затем этот контекст отправляется методу BindComplexModel(...), который проверяет свойство Model на null и создает новый экземпляр типа модели, если это так.

Вот что приводит к сбросу значений.

Итак, заполняются только значения, поступающие через форму / строку запроса / данные маршрута, остальные остаются в своем инициализированном состоянии.

Мне удалось внести очень мало изменений в UpdateCollection(...), чтобы исправить эту проблему.

Вот метод с моими изменениями:

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;

}

4 голосов
/ 26 февраля 2010

Руди Бриденраед только что написал отличный пост , описывающий эту проблему и очень полезное решение. Он переопределяет DefaultModelBinder, а затем, когда он сталкивается с коллекцией для обновления, он фактически обновляет элемент, а не создает его новым, как поведение MVC по умолчанию. При этом поведение UpdateModel () и TryUpdateModel () согласуется как с корневой моделью, так и с любыми коллекциями.

1 голос
/ 11 февраля 2010

Вы только что дали мне идею покопаться в исходном коде ASP.NET MVC 2. Я боролся с этим уже две недели. Я обнаружил, что ваше решение не будет работать с вложенными списками. Я поставил точку останова в методе UpdateCollection, и он никогда не попадал в цель. Кажется, что корневой уровень модели должен быть списком, чтобы этот метод назывался

Это вкратце модель, которая у меня есть ... У меня также есть еще один уровень общих списков, но это всего лишь быстрый пример.

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

Полагаю, мне нужно будет копать глубже, чтобы узнать, что происходит.

UPDATE: UpdateCollection все еще вызывается в asp.net mvc 2, но проблема с исправлением выше связана с этим ЗДЕСЬ

...