2009-07-30 13 views
7

Tôi không chắc đây có phải là lỗi trong lớp DefaultModelBinder hay không. Nhưng UpdateModel thường không thay đổi bất kỳ giá trị nào của mô hình ngoại trừ mô hình mà nó đã tìm thấy phù hợp. Hãy xem ví dụ sau:Gọi UpdateModel với một tập hợp các kiểu dữ liệu phức tạp đặt lại tất cả các giá trị không bị ràng buộc?

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

gì xảy ra là UpdateModel tạo mới đối tượng Person, gán thuộc tính Tuổi Tên & của họ từ ValueProvider và đặt chúng trong danh sách tham số <>, mà làm cho phần còn lại của các thuộc tính được đặt thành giá trị ban đầu mặc định của chúng (ví dụ: Id = 0) vậy điều gì đang xảy ra ở đây?

Trả lời

8

UPDATE: tôi bước qua mã MVC nguồn (đặc biệt là DefaultModelBinder lớp) và đây là những gì tôi thấy:

Lớp xác định chúng tôi đang cố gắng để ràng buộc một bộ sưu tập để nó gọi phương pháp: UpdateCollection(...) mà tạo ra một bên trong ModelBindingContext có thuộc tính nullModel. Sau đó, ngữ cảnh đó được gửi đến phương thức BindComplexModel(...) để kiểm tra thuộc tính Model cho null và tạo một trường hợp mới loại mô hình nếu trường hợp đó xảy ra.

Đó là nguyên nhân khiến giá trị được đặt lại.

Và như vậy, chỉ các giá trị đang đến thông qua dữ liệu chuỗi/truy vấn biểu mẫu/truy vấn được điền, phần còn lại vẫn ở trạng thái ban đầu.

Tôi có thể thực hiện rất ít thay đổi đối với UpdateCollection(...) để khắc phục vấn đề này.

Dưới đây là phương pháp với những thay đổi của tôi:

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

Bạn chỉ cần đưa cho tôi một ý tưởng để thâm nhập vào ASP.NET MVC 2 mã nguồn. Tôi đã đấu tranh với điều này trong hai tuần nay. Tôi phát hiện ra rằng giải pháp của bạn sẽ không hoạt động với danh sách lồng nhau. Tôi đặt một breakpoint trong phương pháp UpdateCollection, và nó không bao giờ bị tấn công. Có vẻ như cấp độ gốc của mô hình cần phải là danh sách cho phương pháp này được gọi là

Đây là mô hình ngắn mà tôi có .. Tôi cũng có một cấp danh sách chung hơn, nhưng đây chỉ là mẫu nhanh ..

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

Tôi đoán rằng, tôi sẽ cần phải tìm hiểu sâu hơn để tìm hiểu những gì đang diễn ra.

UPDATE: Các UpdateCollection vẫn được gọi trong asp.net MVC 2, nhưng vấn đề với việc sửa chữa trên có liên quan đến này HERE

4

Rudi Breedenraed chỉ viết một tuyệt vời post mô tả vấn đề này và một giải pháp rất hữu ích . Ông ghi đè DefaultModelBinder và sau đó khi nó đi qua một bộ sưu tập để cập nhật, nó thực sự cập nhật các mục thay vì tạo ra nó mới như hành vi MVC mặc định. Với điều này, hành vi UpdateModel() và TryUpdateModel() phù hợp với cả mô hình gốc và bất kỳ bộ sưu tập nào.

+0

có đúng với MVC 3 không? – Vidar

+0

@Vidar Có sợ vậy. – nfplee

Các vấn đề liên quan