2009-07-30 4 views
7

Я не уверен, что это ошибка в классе DefaultModelBinder или что. Но UpdateModel обычно не изменяет никаких значений модели, кроме тех, на которые оно найдено. Взгляните на следующее:Вызов 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 создает новые объекты Person, присвоить их имя & Возраст свойства от ValueProvider и поместить их в список аргументов <>, что делает остальную часть свойства установлены на их начальное значение по умолчанию (например, Id = 0) , так что происходит здесь?

ответ

8

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

Класс определяет, мы пытаемся связать коллекцию так он вызывает метод: UpdateCollection(...), который создает внутренний ModelBindingContext, который имеет свойство nullModel. Впоследствии этот контекст отправляется методу 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; 

}

1

Вы только что дал мне идею копаться в 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, но проблема с исправлением выше связано с этим HERE

4

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

+0

это тоже относится к MVC 3? – Vidar

+0

@ Видар Да, боюсь. – nfplee