当我的模型的IEnumerable<T>
属性实现为iterator(即yield return
)时,当传入值使用方括号语法(例如"Foo[0]"
)时,MVC的DefaultModelBinder
无法绑定到该属性。MVC模型绑定:为什么我不能绑定到迭代器属性?
范例模型:
namespace ModelBinderTest
{
using System.Collections.Generic;
public class MyModel
{
private List<string> fooBacking = new List<string>();
public IEnumerable<string> Foo
{
get
{
foreach (var o in fooBacking)
{
yield return o; // <-- ITERATOR BREAKS MODEL BINDING
}
}
set { fooBacking = new List<string>(value); }
}
private List<string> barBacking = new List<string>();
public IEnumerable<string> Bar
{
get
{
// Returning any non-iterator IEnumerable works here. Eg:
return new List<string>(barBacking);
}
set { barBacking = new List<string>(value); }
}
}
}
未按例如:
namespace ModelBinderTest
{
using System;
using System.Linq;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
[CLSCompliant(false)]
public class DefaultModelBinderTestIterator
{
[TestMethod]
public void BindsIterator()
{
// Arrange
var model = new MyModel();
ModelBindingContext bindingContext = new ModelBindingContext()
{
FallbackToEmptyPrefix = true,
ModelMetadata = ModelMetadataProviders
.Current
.GetMetadataForType(null, model.GetType()),
ModelName = "",
ValueProvider = new NameValueCollectionValueProvider(
new System.Collections.Specialized.NameValueCollection()
{
{ "Foo[0]", "foo" },
{ "Bar[0]", "bar" },
},
System.Globalization.CultureInfo.InvariantCulture
)
};
DefaultModelBinder binder = new DefaultModelBinder();
// Act
MyModel updatedModel = (MyModel)binder.BindModel(
new ControllerContext(), bindingContext);
// Assert
Assert.AreEqual(1, updatedModel.Bar.Count(),
"Bar property should have been updated");
Assert.AreEqual("bar", updatedModel.Bar.ElementAtOrDefault(0),
"Bar's first element should have been set");
Assert.AreEqual(1, updatedModel.Foo.Count(),
"Foo property should have been updated");
Assert.AreEqual("foo", updatedModel.Foo.ElementAtOrDefault(0),
"Foo's first element should have been set");
}
}
}
上述单元测试将更新我的模型的Bar
属性为["bar"]
没有问题(带或不带方括号在集合键中),但将无法将任何内容绑定到Foo
属性。
有谁知道(在低层次)为什么将IEnumerable
属性实现为迭代器会导致模型绑定失败?
我没有变通方法,而是一些分析很感兴趣,因为我已经耗尽了我的框架,得到这个地步的知识;)
1:单元测试是为SO隔离问题的最简单方法,而不是通过整个MVC应用程序示例。例如,我知道如果我从输入中删除方括号并为所有值重新使用相同的"Foo"
键,则模型绑定将起作用。然而,真正失败的情况下需要方括号,因为集合中的每个项目都是具有自己的子属性的复杂类型。或者另一种解决方法:将非迭代器IEnumerable<T>
参数添加到操作中,并将指定直接添加到操作内部的属性。啊。
你说得对,实施类似[`yield null`](http://stackoverflow.com/questions/1765400/yield-return-with-null#1765422)证明是另一种解决方法,因为它必须触发模型联编程序分配一个新的列表。两个问题虽然: 1)为什么从输入中删除方括号(即通过`“Foo”`而不是`“Foo [0]”`)工作? 2)`Bar`不返回null。为什么它成功更新? – 2011-02-09 16:13:53