2009-07-30 6 views
7

Nie jestem pewien, czy jest to błąd w klasie DefaultModelBinder lub co. Ale UpdateModel zwykle nie zmienia żadnych wartości modelu z wyjątkiem tych, dla których znaleziono dopasowanie. Spójrz na następujące:Wywołanie UpdateModel z kolekcją złożonych typów danych powoduje zresetowanie wszystkich niepowiązanych wartości?

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

Co dzieje UpdateModel tworzy nowych obiekty osoby, przypisać ich nazwa & właściwości Wiek od ValueProvider i umieścić je na liście argumentów <>, co sprawia, resztę właściwości ustawione na domyślną wartość początkową (np. Id = 0) , więc co się tutaj dzieje?

Odpowiedz

8

UPDATE: Wszedłem za pośrednictwem kodu mvc źródłowego (szczególnie DefaultModelBinder klasa) i oto co znalazłem:

Klasa określa staramy się powiązać kolekcję więc wywołuje metodę: UpdateCollection(...) który tworzy wewnętrznego, który ma właściwość nullModel. Następnie kontekst ten jest wysyłany do metody BindComplexModel(...), która sprawdza właściwość Model dla null i tworzy instancję nową instancję typu modelu, o ile tak jest.

To powoduje resetowanie wartości.

I tak tylko wartości, które przechodzą przez formularz/ciąg zapytania/dane trasy są zapełnione, reszta pozostaje w stanie początkowym.

Udało mi się wprowadzić bardzo niewiele zmian w UpdateCollection(...), aby rozwiązać ten problem.

Oto metoda z moimi zmianami:

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

Po prostu dał mi pomysł kopać w ASP.NET MVC kodu źródłowego 2. Walczę z tym od dwóch tygodni. Okazało się, że twoje rozwiązanie nie działa z listami zagnieżdżonymi. Wprowadziłem punkt przerwania w metodzie UpdateCollection i nigdy nie zostaje trafiony. Wygląda na to, że podstawowym poziomem modelu musi być lista dla tej metody, która ma być nazwana

Jest to w skrócie model, który mam ... Mam też jeszcze jeden poziom ogólnych list, ale to tylko krótka próbka ..

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

Zgaduję, że będę musiała kopać głębiej, aby dowiedzieć się, co się dzieje.

UPDATE: UpdateCollection nadal jest wywoływana w ASP.NET MVC 2, ale problem z wyżej poprawki związane z tym HERE

4

Rudi Breedenraed właśnie napisał doskonałą post opisujący ten problem i bardzo pomocny rozwiązanie . Nadpisuje DefaultModelBinder, a gdy natrafia na kolekcję do aktualizacji, to faktycznie aktualizuje element, zamiast tworzyć go zupełnie jak domyślne zachowanie MVC. Dzięki temu zachowanie UpdateModel() i TryUpdateModel() jest spójne zarówno z modelem root, jak i wszelkimi kolekcjami.

+0

czy to samo dotyczy MVC 3? – Vidar

+0

@Vidar Tak się boję. – nfplee

Powiązane problemy