ASP.Net MVC 2 - 更好的DictionaryBinding for Dictionary< int,int> [英] ASP.Net MVC 2 - better ModelBinding for Dictionary<int, int>

查看:147
本文介绍了ASP.Net MVC 2 - 更好的DictionaryBinding for Dictionary< int,int>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在某些特殊情况下,您将需要一个文本框列表(以处理n-n个关联),该列表的id在运行时间之前不知道。
这样的事情: http://screencast.com/t/YjIxNjUyNmU



在这个特定的示例中,我正在将一个计数关联到我的一些模板。



ASP.Net MVC 1 我编码了一个字典ModelBinder来拥有干净直观的HTML。
它允许这样的东西:

  //循环模板
foreach(模板中的ITemplate模板)
{
//获取文本值
int val;
content.TryGetValue(template.Id,out val);
var value =((val> 0)?val.ToString():string.Empty);

//计算元素名称(用于字典绑定)
string id =cbts_ {0}。FormatMe(template.Id);
%>
< input type =textid =<%= 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>();

//获取值
var values = bindingContext.ValueProvider;
//获取模型名称
string modelname = bindingContext.ModelName +'_';
int skip = modelname.Length;

//在键上循环
foreach(string keyStr in values.Keys)
{
//如果元素已被识别
if( keyStr.StartsWith(modelname))
{
//获取该键
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中完美的工作)。



它看起来像这样: p>

  int counter = 0; 
//模板上的循环
foreach(模板中的ITemplate模板)
{
//获取文本值
int val;
content.TryGetValue(template.Id,out val);
var value =((val> 0)?val.ToString():string.Empty);

//计算元素名称(用于字典绑定)
string id =cbts_ {0}。FormatMe(template.Id);
string dictKey =cbts [{0}]。键.FormatMe(counter);
string dictValue =cbts [{0}]。Value.FormatMe(counter ++);
%>
< input type =hiddenname =<%= dictKey%> value =<%= template.Id%> />
< input type =textid =<%= id%> name =<%= dictValue%> value =<%= value%> />
< label for =<%= id%>><%= template.Name%>< / label>
< br />

在控制器中,我需要欺骗 ModelState 避免需要一个值错误:

  public ActionResult保存(int?id,Dictionary< int,int> cbts )
{
//清除来自modelstate的所有错误
foreach(this.ModelState.Values中的var值)
value.Errors.Clear();

这太棘手了。
我很快就需要使用这种绑定,也许有一些开发人员在应用程序上工作。



问题:




  • 你知道有什么办法让它更好吗?



我需要的是IMO,它是一个用于字典的ModelBinder,允许更好的html,并且不要求所有的值都是必需的。

解决方案

你可以继续在MVC 2中继续使用你的MVC 1模型绑定。你需要做的最大的变化是你的模型绑定不应该违反IValueProvider,而应该直接反对Request.Form。您可以枚举该集合并执行您的特定场景所需的任何逻辑。



IValueProvider接口的目的是提供数据来自哪里的抽象,用于通用粘合剂(如DefaultModelBinder)。由于数据不能保证是可列举的,所以IValueProvider本身不能是IEnumerable。但是在特定的情况下,在特定情况下,您具有特定的绑定器,并且已经知道数据来自表单,抽象是不必要的。


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.

解决方案

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 MVC 2 - 更好的DictionaryBinding for Dictionary&lt; int,int&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆