Привязка к словарю списков списков логических объектов со строго типизированным представлением MVC с помощью флажков

Я использую MVC 4, .Net 4 и Visual Studio 2012.

Я пытаюсь использовать довольно сложную модель с одним из моих представлений, и у меня возникают серьезные проблемы с тем, чтобы заставить его правильно связываться.

Модель включает словарь с целочисленными ключами и значениями, которые являются списками списков bools.

По сути, поиск был выполнен по элементам, обозначенным целым числом, у каждого элемента было несколько поисковых терминов, и для каждого из этих терминов у нас есть список результатов. Я отображаю результаты на странице, и рядом с каждым результатом есть флажок. Для каждого результата пользователь будет указывать, хотят ли они что-то сделать с помощью следующего действия, установив флажок.

На данный момент флажки отображаются правильно, включая предустановленные значения из контроллера, но когда я нажимаю кнопку отправки внизу формы, я получаю эту ошибку:

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.

Мне кажется, что это связано с использованием словаря, который, как мне сказали, не работает как модель. Возможно, мне придется измениться на что-то другое, но я бы предпочел не делать этого, если мне абсолютно не нужно. Похоже, что где-то здесь может быть ответ: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx или список флажков для сложного типа в asp.net mvc, или Как связать параметр типа Dictionary для действий GET и POST в ASP.NET MVC, но я нашел их после того, как вопрос был полностью написан, и я еще не понял его, так что, возможно, кто-нибудь может мне помочь.

Вот вершина трассировки стека:

[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

Вот модель:

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

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

Так как это может быть уместно, вот структура ViewBag.iidToData, которая содержит результаты для отображения:

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; }
    }

Вот некоторые важные части из View с именами переменных, измененными для защиты невинных:

@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>
}

2 ответа

Решение

Хорошо, я понял.

Я попытался использовать Tuple>> вместо словаря>>. Это не удалось, по-видимому, потому что Tuple не имеет конструктора с 0 параметрами.

Затем я попытался использовать пользовательский класс, который имел два свойства: int и List>. Я заставил это работать после некоторой путаницы, и как только это сработало, я смог перепроектировать это и заставить Словарь работать.

Вот рабочая версия (та же модель представления и iidToData, что и раньше):

...

@{
    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++;

}

...

Таким образом, индекс, используемый для скрытого ввода для ключа словаря, не является ключом, а является перечислением пар KeyValue, 0-й, 1-й, 2-й и т. Д. Это тот же индекс, который используется для указания значения позже.

Это приводит нас к другой забавной части. Имя для флажка должно иметь Model.DictionaryName[enumerationIndex].Value, чтобы указать, что мы устанавливаем значение для этой индексированной пары KeyValue.

Кроме того, элемент ввода html, созданный этой вспомогательной функцией, всегда имеет значение true, а второй скрытый ввод всегда имеет значение false. Атрибут "флажок" указывает, отправляется ли значение флажка ввода в связыватель по умолчанию или нет, то есть получает ли оно значение "истина, ложь" или просто "ложь". Затем связующее должным образом интерпретируется как значение bool.

Наконец, скрытый ввод в блоке else в конце добавляет пустой список> для записей, которые не имеют соответствующих результатов поиска..Value соединяется с более ранней.Key, чтобы указать полную пару KeyValue для добавления в словарь. Затем, когда средство связывания видит Model.Dictionary[index].Value[index], даже не видя Model.Dictionary[index].Value[index][index], оно создает пустой список, но не добавляет никаких значений.

Так что это было излишне сложно, но теперь, надеюсь, другие могут использовать словари со значениями Collection в своих моделях представления.

Я должен был сделать что-то вроде этого, и я должен был сделать со значением словаря в массиве десятичных дробей (Dictionary). Если это поможет, я сделал так:

@foreach (var kvp in Model.MyDictionary)
{
    <tr>
        <td>@kvp.Key</td>
        @for (int i = 0; i < kvp.Value.Count(); i++)
        {
            <td>
                <input type="text" name="@("MyDictionary[" + kvp.Key + "]")" value="@kvp.Value[i]" />
            </td>
        }
    </tr>
}
Другие вопросы по тегам