8

我不确定这是 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 创建新的Person 对象,从 ValueProvider 分配它们的 Name 和 Age 属性并将它们放入参数 List<> 中,这使得其余属性设置为其默认初始值(例如 Id = 0)所以什么是在这里进行吗?

4

3 回答 3

9

更新: 我逐步浏览了 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;

}

于 2009-07-30T19:26:36.147 回答
4

Rudi Breedenraed 刚刚写了一篇很好的文章来描述这个问题和一个非常有用的解决方案。他覆盖 DefaultModelBinder,然后当遇到要更新的集合时,它实际上会更新项目,而不是像默认的 MVC 行为那样创建新项目。有了这个,UpdateModel() 和 TryUpdateModel() 行为与根模型和任何集合都是一致的。

于 2010-02-25T21:49:49.813 回答
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;}
}

我想,我需要更深入地了解发生了什么。

更新: UpdateCollection 仍然在 asp.net mvc 2 中被调用,但上面修复的问题与这里有关

于 2010-02-11T15:58:29.160 回答