MVC3字典没有绑定到模型

我有一个包含字典属性的模型。 (这已从一个较大的项目中提取到这个例子中,我已经确认它仍然有同样的问题)

public class TestModel { public IDictionary Values { get; set; } public TestModel() { Values = new Dictionary(); } } 

一个控制器

 public class TestController : Controller { public ActionResult Index() { TestModel model = new TestModel(); model.Values.Add("foo", "bar"); model.Values.Add("fizz", "buzz"); model.Values.Add("hello", "world"); return View(model); } [HttpPost] public ActionResult Index(TestModel model) { // model.Values is null after post back here. return null; // I set a break point here to inspect 'model' } } 

和一个观点

 @using TestMVC.Models @model TestModel @using (Html.BeginForm()) { @Html.EditorFor(m => m.Values["foo"]); 
@Html.EditorFor(m => m.Values["fizz"]);
@Html.EditorFor(m => m.Values["hello"]);
}

这会像这样呈现给浏览器:

  

我遇到的问题是在回发后字典在模型上为空。

  • 我这样做是对,还是有更好的方法?

我需要有一些键值存储,因为我的表单上的字段是可变的,所以我不能使用POCO模型。

阅读Scott hanselman关于该主题的博客文章了解更多详细信息,但同时,为了解决您的问题,只需将您的视图替换为以下内容:

   

对所有部分重复相同的操作,也可以将它放在for循环中:

 @for(i=0;i 

请注意,只有在使用OrderedDictionary时,才能通过索引访问键和值

Scott hanselman展示了如何对Dictionary进行ModelBinding

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

从博客引用

如果签名如下所示:

 public ActionResult Blah(IDictionary stocks) { // ... } 

我们在HTML中给出了这个:

       

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

 @model Dictionary @for (int i = 0; i < 3; i++) { Html.EditorFor(m => m[i].Value) { 

我认为它也可以通过键也可以工作

 Html.EditorFor(m => m.Values["foo"].Value) 

如果你需要绑定一个Dictionary,这样每个值都有一个texbox来编辑它,下面是一种让它工作的方法。 影响HTML中name属性生成方式的真正重要部分是模型表达式,它确保在回发时发生模型绑定。 此示例仅适用于Dictionary。

这篇链接的文章解释了使绑定工作的HTML语法,但它留下了Razor语法来实现这一点非常神秘。 此外,文章的不同之处在于它们允许编辑键和值,并使用整数索引,即使字典的键是字符串,而不是整数。 因此,如果您尝试绑定字典,在确定采用哪种方法之前,首先需要首先评估是否只需要值可编辑,或者键和值都是可编辑的,因为这些方案是完全不同的。

如果您需要绑定到复杂对象,即Dictionary,那么您应该能够为每个属性创建一个文本框,并将表达式钻入属性,类似于文章。

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

  public class SomeVM { public Dictionary Fields { get; set; } } public class HomeController : Controller { [HttpGet] public ViewResult Edit() { SomeVM vm = new SomeVM { Fields = new Dictionary() { { "Name1", "Value1"}, { "Name2", "Value2"} } }; return View(vm); } [HttpPost] public ViewResult Edit(SomeVM vm) //Posted values in vm.Fields { return View(); } } 

CSHTML:

仅限编辑器 (当然您可以添加LabelFor以根据Key生成标签):

 @model MvcApplication2.Controllers.SomeVM @using (Html.BeginForm()) { @Html.ValidationSummary(true) 
SomeVM @foreach(var kvpair in Model.Fields) { @Html.EditorFor(m => m.Fields[kvpair.Key]) //html:
}

编辑键/值:

  @{ var fields = Model.Fields.ToList(); } @for (int i = 0; i < fields.Count; ++i) { //It is important that the variable is named fields, to match the property name in the Post method's viewmodel. @Html.TextBoxFor(m => fields[i].Key) @Html.TextBoxFor(m => fields[i].Value) //generates using integers, even though the dictionary doesn't use integer keys, //it allows model binder to correlate the textbox for the key with the value textbox: // 

正如@Blast_Dan和@gprasant所提到的,模型绑定器期望输入元素的name属性的格式为Property[index].Value ,其中indexintValueKeyValuePair类的属性之一。

不幸的是, @Html.EditorFor以错误的格式生成了这个值。 我写了一个HtmlHelper扩展来将name属性转换为正确的格式:

 public static IHtmlString DictionaryEditorFor(this HtmlHelper Html, Expression> expression, IDictionary dictionary, DictionaryIndexRetrievalCounter counter, string templateName, object additionalViewData) { string hiddenKey = Html.HiddenFor(expression).ToHtmlString(); string editorValue = Html.EditorFor(expression, templateName, additionalViewData).ToHtmlString(); string expText = ExpressionHelper.GetExpressionText(expression); string indexText = expText.Substring(expText.IndexOf('[')).Replace("[", string.Empty).Replace("]", string.Empty); KeyValuePair item = dictionary.SingleOrDefault(p => p.Key.ToString() == indexText); int index = counter.GetIndex(item.Key); string key = hiddenKey.Replace("[" + indexText + "]", "[" + index + "].Key").Replace("value=\"" + item.Value + "\"", "value=\"" + item.Key + "\""); string value = editorValue.Replace("[" + indexText + "]", "[" + index + "].Value"); return new HtmlString(key + value); } 

因为整数索引必须遵循以下规则:

  1. 必须以0开头

  2. 必须是完整的(例如,你不能跳过3到5)

我写了一个计数器类来处理我的整数索引:

 public class DictionaryIndexRetrievalCounter { private IDictionary _dictionary; private IList _retrievedKeys; public DictionaryIndexRetrievalCounter(IDictionary dictionary) { this._dictionary = dictionary; this._retrievedKeys = new List(); } public int GetIndex(TKey key) { if (!_retrievedKeys.Contains(key)) { _retrievedKeys.Add(key); } return _retrievedKeys.IndexOf(key); } }