ASP.Net MVC 2 - 更好的 Dictionary模型绑定
在某些特殊情况下,您将需要一个文本框列表(以处理 n - n 关联),其 id 在运行时未知。 像这样的东西: http://screencast.com/t/YjIxNjUyNmU
在那个特定的示例中,我希望将计数与我的一些“模板”相关联。
在 ASP.Net MVC 1 中,我编写了一个 Dictionary ModelBinder 以获得干净直观的 HTML。 它允许这样的事情:
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
%>
<input type="text" id="<%= id %>" name="<%= id %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
这是绑定器的代码:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IDictionary<int, int> retour = new Dictionary<int, int>();
// get the values
var values = bindingContext.ValueProvider;
// get the model name
string modelname = bindingContext.ModelName + '_';
int skip = modelname.Length;
// loop on the keys
foreach(string keyStr in values.Keys)
{
// if an element has been identified
if(keyStr.StartsWith(modelname))
{
// get that key
int key;
if(Int32.TryParse(keyStr.Substring(skip), out key))
{
int value;
if(Int32.TryParse(values[keyStr].AttemptedValue, out value))
retour.Add(key, value);
}
}
}
return retour;
}
当传递到 ASP.Net MVC 2 时,问题是 ValueProvider 不再是字典了。无法像我那样循环遍历这些值来解析它们。
我没有找到任何其他方法来做到这一点(如果你知道,请告诉我)。
我最终切换到绑定字典的“标准”方式,但是 HTML 很丑陋,违反直觉(使用计数器循环非索引集合??)并且所有值都是必需的,与我需要的行为不同(并且在 ASP.Net MVC 1) 中完美运行。
它看起来像这样:
int counter= 0;
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
string dictKey = "cbts[{0}].Key".FormatMe(counter);
string dictValue = "cbts[{0}].Value".FormatMe(counter++);
%>
<input type="hidden" name="<%= dictKey %>" value="<%= template.Id %>" />
<input type="text" id="<%= id %>" name="<%= dictValue %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
在控制器中,我需要欺骗 ModelState 以避免“需要一个值”错误:
public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
// clear all errors from the modelstate
foreach(var value in this.ModelState.Values)
value.Errors.Clear();
这太棘手了。 我很快将需要多次使用这种绑定,并且可能需要一些级别的开发人员来处理该应用程序。
问题:
- 你知道有什么办法可以让它变得更好吗?
IMO,我需要的是一个字典的 ModelBinder,它允许更好的 html,并且不考虑所有值都是必需的。
In some special cases you will need a list of textboxes (to deal with n - n associations) whose id is not know before runtime.
Something like this : http://screencast.com/t/YjIxNjUyNmU
In that particular sample I'm looking to associate a count to some of my 'templates'.
in ASP.Net MVC 1 I coded a Dictionary ModelBinder to have a clean and intuitive HTML.
It allowed things like this :
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
%>
<input type="text" id="<%= id %>" name="<%= id %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
Here is the code of the binder :
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IDictionary<int, int> retour = new Dictionary<int, int>();
// get the values
var values = bindingContext.ValueProvider;
// get the model name
string modelname = bindingContext.ModelName + '_';
int skip = modelname.Length;
// loop on the keys
foreach(string keyStr in values.Keys)
{
// if an element has been identified
if(keyStr.StartsWith(modelname))
{
// get that key
int key;
if(Int32.TryParse(keyStr.Substring(skip), out key))
{
int value;
if(Int32.TryParse(values[keyStr].AttemptedValue, out value))
retour.Add(key, value);
}
}
}
return retour;
}
When passing to ASP.Net MVC 2, the problem is the ValueProvider is not a dictionary anymore. There is no way to loop through the values to parse them the way I did.
And I did not find any other way to do so (If you know one, tell me).
I finally switched to the 'standard' way of binding the dictionary, but the HTML is ugly, counterintuitive (using counters to loop over a non-indexed collection ??) and all the values are required, unlike the behavior I need (and that worked perfectly in ASP.Net MVC 1).
It looks like this :
int counter= 0;
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
string dictKey = "cbts[{0}].Key".FormatMe(counter);
string dictValue = "cbts[{0}].Value".FormatMe(counter++);
%>
<input type="hidden" name="<%= dictKey %>" value="<%= template.Id %>" />
<input type="text" id="<%= id %>" name="<%= dictValue %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
In the controller I need to trick the ModelState
to avoid 'a value is required' errors :
public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
// clear all errors from the modelstate
foreach(var value in this.ModelState.Values)
value.Errors.Clear();
This is too tricky.
I will soon need to use this kind of binding many times, and maybe have some tiers developer work on the application.
Question :
- Do you know a way to make it better ?
What I need, IMO, is a ModelBinder for dictionary that allow a better html and that does no consider that all values are required.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您完全可以在 MVC 2 中继续使用 MVC 1 模型绑定程序。您必须做出的最大更改是您的模型绑定程序不应该与 IValueProvider 冲突,而应该直接与 Request.Form 冲突。您可以枚举该集合并执行特定场景所需的任何逻辑。
IValueProvider 接口的目的是提供数据来源的抽象,适用于通用绑定器(如 DefaultModelBinder)。由于不保证数据是可枚举的,因此 IValueProvider 本身不能是 IEnumerable。但在您的特定情况下,如果您有针对特定场景的特定绑定器并且已经知道数据来自表单,则抽象是不必要的。
You are absolutely free to continue using your MVC 1 model binder in MVC 2. The biggest change that you'll have to make is that your model binder shouldn't go against the IValueProvider but should instead go directly against Request.Form. You can enumerate over that collection and perform whatever logic is necessary for your particular scenario.
The purpose of the IValueProvider interface is to provide an abstraction over where the data comes from and is meant for a general-purpose binder (like the DefaultModelBinder). As the data isn't guaranteed to be enumerable, the IValueProvider itself cannot be IEnumerable. But in your particular case, where you have a specific binder for a specific scenario and already know the data is coming from the form, the abstraction is unnecessary.
我相信这篇文章会有所帮助 - ASP.NET MVC2 和 MVC3 中的字典模型绑定器
简而言之,解决方案是在 MVC 2-3 中模仿 MVC 1 ValueProvider。
I believe this article will help - Dictionary Model Binder in ASP.NET MVC2 and MVC3
In short the solution is to imitate MVC 1 ValueProvider in MVC 2-3.