2012-05-11 81 views
2

我有一个包含字典属性的模型。 (这已经从一个较大的项目进入这个例子中,我已经证实还是蒸有同样的问题)MVC3字典不绑定到模型

public class TestModel 
{ 
    public IDictionary<string, string> Values { get; set; } 

    public TestModel() 
    { 
     Values = new Dictionary<string, string>(); 
    } 
} 

控制器

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"]); 
    <br /> 
    @Html.EditorFor(m => m.Values["fizz"]); 
    <br /> 
    @Html.EditorFor(m => m.Values["hello"]); 
    <br /> 
    <input type="submit" value="submit" /> 
} 

这这样呈现给浏览器:

<input class="text-box single-line" id="Values_foo_" name="Values[foo]" type="text" value="bar" /> 

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

  • 我是对的吗,还是有更好的办法?

我需要有某种键值存储,因为我的窗体上的字段是可变的,所以我不能使用POCO模型。

+0

好像这个家伙对键值对http://stackoverflow.com/questions/1300642/asp-mvc-net-how-to-bind-keyvaluepair解决 希望这将制定出一个字典 –

+0

@Blast_dan。有趣。字典只是KeyValuePairs的一个集合,所以使用像这样的结构会导致问题是有道理的。我会尝试使用自定义的KeyValuePair类来查看它是否有效。 –

+1

试试EditorTemplate方法,我用List对它进行了测试,检查它是否可以在字典上工作http://www.ienablemuch.com/2012/04/aspnet-mvc-editor-templates.html –

回答

0

由于索引访问键和值提到@Blast_Dan和@gprasant时,模型联编程序期望输入元素的name属性的格式为Property[index].Value,其中indexintValueKeyValuePair类中的一个属性。

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

public static IHtmlString DictionaryEditorFor<TModel, TProperty, TKey, TValue>(this HtmlHelper<TModel> Html, Expression<Func<TModel, TProperty>> expression, IDictionary<TKey, TValue> dictionary, DictionaryIndexRetrievalCounter<TKey, TValue> 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<TKey, TValue> 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<TKey, TValue> 
{ 
    private IDictionary<TKey, TValue> _dictionary; 
    private IList<TKey> _retrievedKeys; 

    public DictionaryIndexRetrievalCounter(IDictionary<TKey, TValue> dictionary) 
    { 
     this._dictionary = dictionary; 
     this._retrievedKeys = new List<TKey>(); 
    } 

    public int GetIndex(TKey key) 
    { 
     if (!_retrievedKeys.Contains(key)) 
     { 
      _retrievedKeys.Add(key); 
     } 

     return _retrievedKeys.IndexOf(key); 
    } 
} 
1

斯科特Hanselman的显示怎么办ModelBinding从博客

字典

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

报价如果签名是这样的:

public ActionResult Blah(IDictionary<string, Company> stocks) { 
    // ... 
} 

我们给出这在HTML:

<input type="text" name="stocks[0].Key" value="MSFT" /> 
<input type="text" name="stocks[0].Value.CompanyName" value="Microsoft Corporation" /> 
<input type="text" name="stocks[0].Value.Industry" value="Computer Software" /> 
<input type="text" name="stocks[1].Key" value="AAPL" /> 
<input type="text" name="stocks[1].Value.CompanyName" value="Apple, Inc." /> 
<input type="text" name="stocks[1].Value.Industry" value="Consumer Devices" /> 

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

@model Dictionary<string, string> 

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

我想这也将被重点工作,以及如

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

这个问题是我生成的html在输入名称属性中不包含'.Key'和'.Value'。 –

+0

你不能使用这样的东西吗? @ Html.TextBoxFor(model => model.Values,new {name =“foo [0] .Value”}) –

+0

或@ Html.EditorFor(model => model,“TemplateName”,“foo [0] .Value “)应该也可以工作 –

2

通过斯科特Hanselman的博客文章阅读的话题更多的细节,但在平均时间, 为了解决您的问题,只需将您的视图替换为以下内容:

<input type="hidden" name="Values[0].Key" value="foo" /> 
<input type="text" name="Values[0].Value" value="bar" /> 

重复相同的所有部分,也许把它放在一个for循环,如:

@for(i=0;i<Model.Values.Count;i++) 
{ 
    @Html.Hidden("Values[@i].Key", @Model.Values.Keys[@i]) 
    @Html.TextBox("Values[@i].Value", @Model.Values.Values[@i]) 
} 

注意,您可以通过仅当您使用的OrderedDictionary

+0

我不想自己生成html,我想使用'@ Html.EditorFor'。有没有什么方法可以在斯科特的文章中使用它并实现输出? –

+0

那么,你可以编写你自己的Html帮助程序来为你生成这个Html Helper,但是它不会对其他场景非常有用。 不,你不能实现这个使用EditorFor() – gprasant

+0

结帐,如果我已经添加上面的助手将为你工作 – gprasant

1

,使得每个值有texbox进行编辑,下面是使它工作的一种方式。影响HTML中名称属性的真正重要部分是模型表达式,它确保模型绑定在回发时发生。本示例仅适用于Dictionary。

链接的文章解释了使绑定起作用的HTML语法,但它留下了Razor语法来完成这个很神秘的事情。另外,文章的区别在于它们允许编辑键和值,并且即使字典的键是字符串而不是整数,也使用整数索引。因此,如果您尝试绑定字典,则在决定采用哪种方法之前,您确实需要首先评估是否只需要值为可编辑值,或者同时使用键和值,因为这些场景完全不同。

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

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

public class SomeVM 
    { 
     public Dictionary<string, string> Fields { get; set; } 
    } 

    public class HomeController : Controller 
    { 
     [HttpGet] 
     public ViewResult Edit() 
     { 
      SomeVM vm = new SomeVM 
      { 
      Fields = new Dictionary<string, string>() { 
        { "Name1", "Value1"}, 
        { "Name2", "Value2"} 
       } 
      }; 

      return View(vm); 

     } 

     [HttpPost] 
     public ViewResult Edit(SomeVM vm) //Posted values in vm.Fields 
     { 
      return View(); 
     } 
    } 

CSHTML:

编辑的值仅(当然你可以添加LabelFor基于密钥生成标签):

@model MvcApplication2.Controllers.SomeVM 

@using (Html.BeginForm()) { 
    @Html.ValidationSummary(true) 

    <fieldset> 
     <legend>SomeVM</legend> 

     @foreach(var kvpair in Model.Fields) 
     { 
      @Html.EditorFor(m => m.Fields[kvpair.Key]) //html: <input name="Fields[Name1]" …this is how the model binder knows during the post that this textbox value gets stuffed in a dictionary named “Fields”, either a parameter named Fields or a property of a parameter(in this example vm.Fields). 
     } 

     <p> 
      <input type="submit" value="Save" /> 
     </p> 
    </fieldset> 
} 

编辑都键/值:

@{ 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:    
     //<input name="fields[0].Key" ... 
     //<input name="fields[0].Value" ... 

     //You could even use javascript to allow user to add additional pairs on the fly, so long as the [0] index is incremented properly 
    }