Asp.net MVC2 IModelBinder正试图推动我坚果(以及随后的)
比方说,我有Asp.net MVC2 IModelBinder正试图推动我坚果(以及随后的)
class FooClass { }
class BarClass
{
public FooClass Foo;
}
这BarClass是我传递到的ViewPage模型。
我也通过(通过ViewData)一个IEnumerable<SelectListItem>
与所有的Foo,并匹配bar.Foo
被选中(在运行时检查)。
然后我打电话Html.DropDownList("Foo", foos);
的下拉列表呈现很好,但它并没有选择合适的项目,因为HTML控件有属性的名称,它与运行internaly的ViewData.Eval()
食堂。这似乎是一个公认的行为(见有关如此多的答案),所以我并不认为有关和呼叫更改为扩展名:
Html.DropDownList("DDL_Foo", foos);
正确的价值选择和我快乐。所以我把表格寄回。
不幸的是,在我的控制器的相应操作中,Foo成员为空。所以我添加一个FooModelBinder
,它实现了IModelBinder
来拦截表单的DDL_Foo并正确地初始化FooClass。
但是FooModelBinder.BindModel
从不被解雇,bar.Foo
为空。如果我再次更改我的视图并将下拉列表重新命名为Foo,则FooModelBinder会按预期触发,并且bar.Foo会根据需要进行初始化。
那么,我错过了什么?更重要的是,我该如何做到这一点。我想到了大量的黑客和解决方法,但这不是我想要的。我想知道如何做对。
谢谢!
[编辑] 感谢您的反馈,但我不认为前缀是问题。
关于Binder,我添加它,因为它不能被正确地初始化,否则。请注意,我正在处理的真实案例比这里介绍的更复杂。这个解决方案只是我能做的最小的模型来重现问题。
这将在revelant代码问(或download the full solution):
控制器
[HttpGet]
public ActionResult Index()
{
var dp = new DummyProvider();
var bar = dp.GetBar();
var foos = new List<SelectListItem>();
dp.GetAllFoos().ForEach(
f => foos.Add(new SelectListItem {Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id }));
ViewData["foos"] = foos;
return View(bar);
}
[HttpPost]
public ActionResult Index(BarClass bar)
{
var dp = new DummyProvider();
var foos = new List<SelectListItem>();
dp.GetAllFoos().ForEach(
f => foos.Add(new SelectListItem { Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id }));
ViewData["foos"] = foos;
ViewData["selectedItem"] = bar.Foo.Name;
return View(bar);
}
VIEW
<%
var foos = ViewData["foos"] as List<SelectListItem>;
using(Html.BeginForm())
{
%>
<p>
<h3>Enter Another Value</h3>
<%= Html.TextBox("AnotherValue", Model.AnotherValue) %>
</p>
<p>
<h3>Enter Yet Another Value</h3>
<%= Html.TextBox("YetAnotherValue", Model.YetAnotherValue) %>
</p>
<p>
<h3>Choose a foo</h3>
<%= Html.DropDownList("DDL_Foo", foos)%>
</p>
<button type="submit">Send back !</button>
<%
}
%>
模型
public class BarClass
{
public FooClass Foo { get; set; }
public string AnotherValue { get; set; }
public string YetAnotherValue { get; set; }
}
public class FooClass
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class FooClassCollection : List<FooClass> { }
public class FooModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var foo = new FooClass();
var guid = Guid.Empty;
if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid))
{
foo.Id = guid;
}
return foo;
}
}
public class DummyProvider
{
public FooClassCollection GetAllFoos()
{
return new FooClassCollection
{
new FooClass {Name = "Item 1", Id = new Guid("4a402abd-ab85-4065-94d6-d9fcc0f9b69e")},
new FooClass {Name = "Item 2", Id = new Guid("cf20bfd6-0918-4ffc-a6ec-c4cc4ed30e7f")},
new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")},
new FooClass {Name = "Item 4", Id = new Guid("1511c15d-9ae4-4b18-9e10-e02588c21b27")},
new FooClass {Name = "Item 5", Id = new Guid("855e4a2f-fc5b-4117-a888-1dc3ebb990fc")},
};
}
public BarClass GetBar()
{
return new BarClass
{
AnotherValue = "Nice value",
YetAnotherValue = "This one is awesome",
Foo = new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")}
};
}
}
GLOBAL。ASAX
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(FooClass), new FooModelBinder());
}
[编辑]有一个悬而未决的问题上codeplex开放,如果你想解决,请选它,请(即使它已经将近一年,现在已经开放)。
我设法让一切由制作BarClassModelBinder做所有岗位上工作。代码如下:
public class BarModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var bar = new BarClass();
// In real code, check for nulls, etc.
bar.AnotherValue = controllerContext.HttpContext.Request.Form["AnotherValue"];
bar.YetAnotherValue = controllerContext.HttpContext.Request.Form["YetAnotherValue"];
var guid = Guid.Empty;
if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid))
{
bar.Foo = new FooClass {Id = guid};
}
return bar;
}
}
所以我在控制器中使用FormCollection时看到的唯一好处是代码的清晰度。唯一让我感到不舒服的是字段名称在ModelBinder中是“隐藏的”,所以如果有人改变了视图,他必须非常小心字段名称。也许有一些方法可以绕过这个问题,也许有一个属性。但即使没有这一点,这是一个较小的邪恶,所以我会解决它。
整个问题仍然看起来像DropDownListFor实现的不良副作用。
刚花了半个小时玩这个。我不会为编写自定义模型活页夹而烦恼。我只是使用视图模型而不是整个FooClass
,而是使用Guid FooId
。反正你不会从下拉列表中获得更多的信息。然后这将工作:
<%: Html.DropDownListFor(m => m.FooId, foos) %>
当您回发它将正确绑定FooId
属性。
如果BarClass
是域模型类,视图模型看起来是这样的(OBV):
public class BarViewModel
{
public Guid FooId { get; set; }
public string AnotherValue { get; set; }
public string YetAnotherValue { get; set; }
}
谢谢你的建议,但这不是我正在寻找的解决方案。这实际上是我们到目前为止使用的解决方案,但我想摆脱那些无用的特定模型。例如,如果我编写我自己的DropDownList,它可以很好地工作。这个问题出现在System.Web.Mvc.Html.SelectExtensions的“SelectInternal”中,它在那里得到htmlHelper.ViewData.Eval(name);出于我不明白的原因,然后重新初始化Selectitem列表中项目的Select值,这是我没有得到的原因。我认为这是为它增加更多的“魔力”。我使用定制的DDL升级了解决方案。 – 2010-07-31 09:02:26
我同意它的行为非常怪异。我也被它弄糊涂了。 – Necros 2010-07-31 14:30:03
你应该发布一些代码。我的猜测是,你的命名在对象定义的某个地方是错误的,而你根本不需要自定义联编程序。 – 2010-07-30 16:55:04
看看我的相关问题,它可能会帮助你http://stackoverflow.com/questions/2563685/need-help-using-the-defaultmodelbinder-for-a-nested-model有一个类似的问题,它证明了命名约定。 – Will 2010-07-30 17:14:21
感谢您的反馈。我按要求添加了代码。 – 2010-07-30 20:14:09