通过使用复选框绑定到一个强类型MVC视图BOOLS名单列表的字典 [英] Bind to a Dictionary of Lists of Lists of Bools with a strongly-typed MVC view by using Checkboxes

查看:124
本文介绍了通过使用复选框绑定到一个强类型MVC视图BOOLS名单列表的字典的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用MVC 4,.NET 4.0和Visual Studio 2012中。

我试图用一个相当复杂的模型,我的意见之一,我有严重的问题得到它正确绑定。

该模型封装了字典整数键,和值是布尔变量的列表的列表。

基本上,搜索是在由整数表示的项目完成后,每一个项目具有几个搜索条件,并为每个这些术语,我们有结果的列表。我在页面上显示的结果,并具有每个结果旁边的复选框。对于每个结果,用户将表明他们是否想要一些东西,旁边的框下一个动作完成。

目前,复选框正常显示,包括来自控制器的preSET值,但是当我preSS在窗体底部的提交按钮,我得到这个错误:

 指定的转换无效。
说明:执行当前Web请求的执行过程中发生未处理的异常。请查看有关错误的详细信息的堆栈跟踪以及它起源于code。异常详细信息:System.InvalidCastException:指定的转换无效。

在我看来,有什么做用字典,其中有人告诉我不会为模型很好地工作。我可能要改变别的东西,但我宁愿没有,除非我绝对有。看起来有可能是一个答案这里的某个地方: HTTP ://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx ,或<一个href=\"http://stackoverflow.com/questions/12583198/checkbox-list-for-complex-type-in-asp-net-mvc\">Checkbox名单在asp.net的MVC 复杂类型,或<一个href=\"http://stackoverflow.com/questions/6858061/how-to-bind-dictionary-type-parameter-for-both-get-and-post-action-on-asp-net-mv\">How绑定字典类型参数为GET和ASP.NET MVC POST操作,但我发现这些后,我有问题的所有写了,我还没有想通出来呢,也许有人可以给我一个手。

这里的堆栈跟踪的顶部:

  [InvalidCastException的:指定的转换是无效的。]
   System.Web.Mvc.CollectionHelpers.ReplaceDictionaryImpl(IDictionary`2字典,IEnumerable`1 newContents)+131[TargetInvocationException:异常已被调用的目标引发异常。]
   System.RuntimeMethodHandle.InvokeMethod(对象的目标,对象[]参数,签名的签名,布尔构造器)+ 0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(obj对象,对象[]参数,对象[]参数)+92
   System.Reflection.RuntimeMethodInfo.Invoke(obj对象,的BindingFlags invokeAttr,粘结剂粘合,对象[]参数,CultureInfo的文化)+108
   System.Reflection.MethodBase.Invoke(obj对象,对象[]参数)+19
   System.Web.Mvc.CollectionHelpers.ReplaceDictionary(类型关键字类型,类型值类型,对象字典,对象newContents)+178

下面的模型:

 公共类AutoResolveModel {
    公共字典&LT; INT,列表与LT;名单&LT;布尔&GT;&GT;&GT; SelectedResults {搞定;组; }    公共AutoResolveModel(){
        SelectedResults =新词典&LT; INT,列表与LT;名单&LT;布尔&GT;&GT;&GT;();
    }
}

由于这可能是相关的,这里的ViewBag.iidToData的结构,其中包含要显示的结果:

 在控制器中的操作:    VAR iidToData =新词典&LT; INT,列表与LT; ItemSearchResult&GT;&GT;();
    ViewBag.iidToData = iidToData;别处:    公共类ItemSearchResult {
        公共字符串C {搞定;组; }
        公共串S {搞定;组; }
        公开名单&LT; INT&GT; SS {搞定;组; }
        公开名单&LT; INT&GT; KS {搞定;组; }
    }

下面是从变量名查看一些相关的部分改变,以保护无辜:

  @model AutoResolveModel@ {
    串机号;
    提交SUBM;
    tblSignatures SIG的;
    ItemSearchResult结果;    变种DC =新CloudDataContext();
}@using(Html.BeginForm(MyAction,myController的新{P =(INT?)ViewBag.l},FormMethod.Post)){    的foreach(KeyValuePair&LT; INT,列表与LT; ItemSearchResult&GT;&GT;千伏ViewBag.iidToData){        &LT;输入类型=隐藏的名字=@(SelectedResults [+ kv.Key +]。重点)值=@ kv.Key/&GT;        ID =(
            ...
        )。单();        &LT; H3&GT;库存项目@ ID&LT; / H3 GT&;        对(INT ISR = 0; ISR&下; kv.Value.Count(); ISR ++){            结果= kv.Value [ISR]            &LT; H4&GT;从@ result.C&LT搜索:@ result.S; / H4&GT;            &LT;表格边框=0&GT;
                <tr><th>K</th><th>I</th><th>V</th><th>G</th><th>D</th><th>S</th><th>T</th></tr>
                @for(INT I = 0; I&LT; result.Ks.Count();我++){
                    SUBM =(
                        ...
                    ).FirstOrDefault();
                    尝试{
                        SIG =(
                            ...
                        )。单();
                    } {抓
                        SIG = NULL;
                    }                    如果(SUBM = NULL&放大器;!&安培;!subm.K = 0){                        &所述; TR&GT;
                            &所述; TD&GT; @ Html.CheckBoxFor(M =&GT; m.SelectedResults [kv.Key] [ISR] [I])下; / TD&GT;
                            &所述; TD&GT; @ result.Ks [1] - ; / TD&GT;
                            &LT; TD&GT; @ subm.i&LT; / TD&GT;
                            &LT; TD&GT; @ subm.v&LT; / TD&GT;
                            &LT; TD&GT; @ subm.g&LT; / TD&GT;
                            &LT; TD&GT; @ subm.d&LT; / TD&GT;
                            @if(SIG!= NULL){
                                &LT; TD&GT; @ sig.S&LT; / TD&GT;
                                &LT; TD&GT; @ sig.T&LT; / TD&GT;
                            }其他{
                                &LT; TD&GT; N / A&LT; / TD&GT;
                                &LT; TD&GT; N / A&LT; / TD&GT;
                            }
                        &LT; / TR&GT;
                    }
                }
            &LT; /表&gt;
        }
    }    &LT;按钮式=提交&GT;搜索和LT; /按钮&GT;
}


解决方案

好吧,我知道了。

我试图用一个元组LT; INT,列表与LT;名单&LT;布尔>>>而不是字典&LT; INT,列表与LT;名单&LT;布尔>>>。失败,显然是因为元组不具有0参数的构造

然后,我试图用有两个属性,一个int和一个列表℃的自定义类;列表与LT;布尔>>。我得到了一些摆弄后工作,一旦工作,我能够进行逆向工程,并得到了解释工作。

这里的工作版本(同一视图模型和iidToData如前):

  ...@ {
    串机号;
    提交SUBM;
    tblSignatures SIG的;
    ItemSearchResult结果;    INT kvInd = 0;    变种DC =新CloudDataContext();
}...的foreach(KeyValuePair&LT; INT,列表与LT; ItemSearchResult&GT;&GT;千伏ViewBag.iidToData){    ...    &LT;输入类型=隐藏的名字=@(Model.SelectedResults [+ kvInd +]。重点)值=@ kv.Key/&GT;    对(INT ISR = 0; ISR&下; kv.Value.Count(); ISR ++){        ...        @if(result.Keytbls.Any()){            的for(int i = 0; I&LT; result.Keytbls.Count();我++){                ...                &LT; TD&GT; @ Html.CheckBox(Model.SelectedResults [+ kvInd +] .value的[+ ISR +] [+ I +],Model.SelectedResults [kv.Key] [ISR] [ I])LT; / TD&GT;                ...        }其他{
            &LT; TR&GT;&LT; TD&GT;&LT;输入类型=隐藏的名字=@(Model.SelectedResults [+ kvInd +] .value的[+ ISR +])/&GT;&LT; / TD&GT;&LT; / TR&GT;
        }        ...
    }    kvInd ++;}...

所以,对字典的键隐藏的输入使用的索引不是关键,但不是键值对,0号之一,一日一,二日一的枚举,等等。这是用来指示以后的值相同的索引

这使我们的另一个有趣的一部分。对于复选框的名称需要有Model.DictionaryName [enumerationIndex] .value的它表明,我们正在为索引的键值对设定的值。

另外,由辅助函数生成的HTML input元素始终具有值为true,第二个隐藏的投入总是有false值。的选中属性表示输入复选框的值是否被发送到默认粘合剂与否,即,是否它得到的真,假或者假的值。这是再由粘结剂为bool值PTED妥善间$ P $。

最后,在结束else块隐藏的输入增加了空List&LT;名单,LT;布尔&GT;&GT;对于没有匹配的搜索结果条目。与先前的。重点的。价值对指示要添加到词典中的完整KEYVALUE对。然后,当粘合剂看到Model.Dictionary [指数] .value的[指数]而没有看到Model.Dictionary [指数] .value的[指数] [指数],它使一个空的列表,但不添加任何值。

所以这是不必要的复杂,但现在希望其他人可以在他们的ViewModels使用具有收藏价值词典。

I'm using MVC 4, .Net 4, and Visual Studio 2012.

I'm trying to use a fairly complex model with one of my views, and I'm having serious trouble getting it to bind properly.

The model wraps a Dictionary with integer Keys, and Values that are Lists of Lists of bools.

Basically, a search was done on items indicated by the integer, each item had several search terms, and for each of those terms we have a list of results. I display the results on a page, and have a checkbox next to each result. For each result, the user will indicate whether they want some stuff done by the next Action by checking the box.

At the moment, the checkboxes display properly, including the preset values from the controller, but when I press the submit button at the bottom of the form, I get this error:

Specified cast is not valid.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidCastException: Specified cast is not valid.

It appears to me to have something to do with using a Dictionary, which I'm told doesn't work well as a model. I may have to change to something else, but I'd rather not unless I absolutely have to. Seems like there might be an answer here somewhere: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx, or Checkbox list for complex type in asp.net mvc, or How to bind Dictionary type parameter for both GET and POST action on ASP.NET MVC, but I found those after I had the question all written up and I haven't figured it out yet, so maybe somebody can give me a hand.

Here's the top of the Stack Trace:

[InvalidCastException: Specified cast is not valid.]
   System.Web.Mvc.CollectionHelpers.ReplaceDictionaryImpl(IDictionary`2 dictionary, IEnumerable`1 newContents) +131

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +92
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +108
   System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19
   System.Web.Mvc.CollectionHelpers.ReplaceDictionary(Type keyType, Type valueType, Object dictionary, Object newContents) +178

Here's the model:

public class AutoResolveModel {
    public Dictionary<int, List<List<bool>>> SelectedResults { get; set; }

    public AutoResolveModel() {
        SelectedResults = new Dictionary<int, List<List<bool>>>();
    }
}

Since it might be relevant, here's the structure of ViewBag.iidToData, which holds the results to be displayed:

In the controller action:

    var iidToData = new Dictionary<int, List<ItemSearchResult>>();
    ViewBag.iidToData = iidToData;

Elsewhere:

    public class ItemSearchResult {
        public string C { get; set; }
        public string S { get; set; }
        public List<int> Ss { get; set; }
        public List<int> Ks { get; set; }
    }

Here's some relevant parts from the View with variable names changed to protect the innocent:

@model AutoResolveModel

@{
    string machineID;
    Submission subm;
    tblSignatures sig;
    ItemSearchResult result;

    var dc = new CloudDataContext();
}

@using( Html.BeginForm( "MyAction", "MyController", new { p = (int?) ViewBag.l }, FormMethod.Post ) ) {

    foreach( KeyValuePair<int, List<ItemSearchResult>> kv in ViewBag.iidToData ) {

        <input type="hidden" name="@("SelectedResults[ " + kv.Key + " ].Key")" value="@kv.Key" />

        ID = (
            ...
        ).Single();

        <h3>Inventory Item @ID</h3>

        for(int isr = 0; isr < kv.Value.Count(); isr++) {

            result = kv.Value[ isr ];

            <h4>Searched for @result.S from @result.C</h4>

            <table border="0">
                <tr><th>K</th><th>I</th><th>V</th><th>G</th><th>D</th><th>S</th><th>T</th></tr>
                @for( int i = 0; i < result.Ks.Count(); i++ ) {
                    subm = (
                        ...
                    ).FirstOrDefault();
                    try {
                        sig = (
                            ...
                        ).Single();
                    } catch {
                        sig = null;
                    }

                    if( subm != null && subm.K != 0 ) {

                        <tr>
                            <td>@Html.CheckBoxFor(m => m.SelectedResults[kv.Key][isr][i])</td>
                            <td>@result.Ks[ i ]</td>
                            <td>@subm.i</td>
                            <td>@subm.v</td>
                            <td>@subm.g</td>
                            <td>@subm.d</td>
                            @if( sig != null ) {
                                <td>@sig.S</td>
                                <td>@sig.T</td>
                            } else {
                                <td>N/A</td>
                                <td>N/A</td>
                            }
                        </tr>
                    }
                }
            </table>
        }
    }

    <button type="submit">Search</button>
}

解决方案

Alright, I got it.

I tried using a Tuple<int, List<List<bool>>> instead of a Dictionary<int, List<List<bool>>>. That failed, apparently because the Tuple doesn't have a 0-parameter constructor.

Then, I tried using a custom class that had two properties, an int and a List<List<bool>>. I got that to work after some fiddling, and once that worked I was able to reverse engineer it and get the Dictionary to work.

Here's the working version (same view model and iidToData as before):

...

@{
    string machineID;
    Submission subm;
    tblSignatures sig;
    ItemSearchResult result;

    int kvInd = 0;

    var dc = new CloudDataContext();
}

...

foreach( KeyValuePair<int, List<ItemSearchResult>> kv in ViewBag.iidToData ) {

    ...

    <input type="hidden" name="@("Model.SelectedResults[" + kvInd + "].Key")" value="@kv.Key" />

    for(int isr = 0; isr < kv.Value.Count(); isr++) {

        ...

        @if(result.Keytbls.Any()) {

            for( int i = 0; i < result.Keytbls.Count(); i++ ) {

                ...

                <td>@Html.CheckBox( "Model.SelectedResults[" + kvInd + "].Value[" + isr + "][" + i + "]",  Model.SelectedResults[ kv.Key ][ isr ][ i ] )</td>

                ...

        } else {
            <tr><td><input type="hidden" name="@("Model.SelectedResults[" + kvInd + "].Value[" + isr + "]")" /></td></tr>
        }

        ...
    }

    kvInd++;

}

...

So, the index used on the hidden input for the dictionary key isn't the key, but is instead an enumeration of the KeyValue pairs, 0th one, 1st one, 2nd one, so on. This is the same index used to indicate the value later on.

That leads us to another funny part. The name for the checkbox needs to have Model.DictionaryName[enumerationIndex].Value in it to indicate that we are setting the value for that indexed KeyValue pair.

Also, the html input element produced by that helper function always has a value of true, and the second hidden input always has a value of false. The "checked" attribute indicates whether the value of the input checkbox is sent to the default binder or not, i.e. whether it gets the value of "true, false" or just "false". This is then properly interpreted by the binder as a bool value.

Finally, the hidden input in the else block at the end adds an empty List<List<bool>> for entries that had no matching search results. The .Value pairs with the earlier .Key to indicate a full KeyValue pair to be added to the dictionary. Then, when the binder sees Model.Dictionary[index].Value[index] without ever seeing Model.Dictionary[index].Value[index][index], it makes an empty list but doesn't add any values.

So that was unnecessarily complicated, but now hopefully others can use Dictionaries with Collection values in their ViewModels.

这篇关于通过使用复选框绑定到一个强类型MVC视图BOOLS名单列表的字典的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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