Roa*_*rth 6 asp.net-mvc ienumerable iterator model-binding
当我的模型具有IEnumerable<T>作为迭代器(即yield return)实现的DefaultModelBinder属性时,当传入值使用方括号语法(例如"Foo[0]")时,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); }
}
}
}
Run Code Online (Sandbox Code Playgroud)
失败的例子1:
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面的单元测试将更新Bar我的模型的属性["bar"]没有问题(在集合键中有或没有方括号),但将无法将任何东西绑定到Foo属性.
有没有人知道(在低级别)为什么将IEnumerable属性实现为迭代器会导致模型绑定失败?
我对解决方法2并不是很感兴趣,而是对一些分析感兴趣,因为我已经用尽了我对框架的了解,这一点远远不够;)
1:单元测试是分离SO问题的最简单方法,而不是通过整个MVC应用程序示例.
2:例如,我知道如果我从输入中删除方括号并"Foo"为所有值重用相同的键,则模型绑定将起作用.然而,真正的失败案例需要方括号,因为集合中的每个项目都是具有自己的子属性的复杂类型.或其他解决方法:添加非迭代器IEnumerable<T>参数的动作,并指定该直接行动里面的财产.啊.
真的很简单。如果它不为空,则DefaultModelBinder不会覆盖您的实例。IEnumerable<>如果它为空,它将创建一个新的List<T>并填充它。
如果它非空,则它知道如何处理某些类型的列表。如果您的列表实现了ICollection<>,那么它将填充它。但是您的实例(带有yield)根本无法更新!
如果您对覆盖感到满意,foobacking那么您可以通过编写自定义模型绑定器来解决此问题。
| 归档时间: |
|
| 查看次数: |
943 次 |
| 最近记录: |