1

私はMVC3コントローラーアクションを次のように定義しました:

public ActionResult _UpdateFilter(int a, string b, List<string> c)

それはうまくいきました。UIのユーザーアクションによっては、アクションにを送信することもあるという新しい要件があります。Dictionary<int, bool>

その辞書にデータを入力するために、次のように適切なフォームに非表示フィールドを追加します。

<input type="hidden" id="id-@(something)-Key" name="sometimesSet[@(idx)].Key" value="@(myIntValue)" />
<input type="hidden" id="id-@(something)-Value" name="sometimesSet[@(idx)].Value" value="@(myBoolValue)" /> 

コントローラーアクションを次のように拡張しました。

public ActionResult _UpdateFilter(int a, string b, List<string> c, Dictionary<int, bool> sometimesSet = null)

ここまでは順調ですね。hiddenフォームに少なくとも1つのキーと値のペアを入力する限り。そうしないと、アクションが呼び出されず、例外が発生します。

Value cannot be null.
Parameter name: key

(質問の最後に長いスタックトレース)。

質問

ルートマッピングを収集しましたが、それsometimesSetがオプションであることがわかりませんが、それを適切に構成する方法がわかりません。それ、どうやったら出来るの?Global.asaxのルート定義を編集していません。

スタックトレース

at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.ContainsKey(TKey key)
at System.Web.Mvc.ValueProviderUtil.GetKeysFromPrefix(IEnumerable`1 collection, String prefix)
at System.Web.Mvc.DictionaryValueProvider`1.GetKeysFromPrefix(String prefix)
at System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefixFromProvider(IValueProvider provider, String prefix)
at System.Web.Mvc.ValueProviderCollection.<>c__DisplayClass11.<GetKeysFromPrefix>b__c(IValueProvider provider)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefix(String prefix)
at System.Web.Mvc.DefaultModelBinder.UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType)
at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<BeginInvokeAction>b__1e(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.<>c__DisplayClass1d.<BeginExecuteCore>b__17(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__3(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<BeginProcessRequest>b__2()
at System.Web.Mvc.SecurityUtil.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust[TResult](Func`1 func)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
4

1 に答える 1

1

Dictionary<K,V>最もクリーンな解決策は、ワイヤ上に関連する値が見つからない場合でも、データを設定する方法を理解する独自のModelBinderを実装することです(空を返します) Dictionary<K,V>

これがコードです

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace My.ModelBinders
{
    /// <summary>
    /// Binds to a generic Dictionary using the basic format outlined at 
    /// http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
    /// with the relaxation that the indices can be arbitrary integers (need not start at 0 or be sequential).
    /// Returns an empty dictionary of no matching parameters found on the wire rather than throwing
    /// an Exception as the current default binder does.
    /// </summary>
    /// <typeparam name="K">Key type</typeparam>
    /// <typeparam name="V">Value type</typeparam>
    public class OptionalDictionaryBinder<K,V> : CustomBinderBase, IModelBinder
    {
        /// <summary>
        /// Pull key/value pairs out of request.  Modified from
        /// https://github.com/loune/MVCStuff/blob/master/Extensions/DefaultDictionaryBinder.cs
        /// Files not currently supported.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private IEnumerable<KeyValuePair<string, string>> GetValueProviderData(ControllerContext context)
        {
            foreach (var fk in context.HttpContext.Request.Form.Keys)
            {
                yield return new KeyValuePair<string, string>((string)fk, context.HttpContext.Request.Form[(string)fk]);
            }

            foreach (var rd in context.RouteData.Values)
            {
                yield return new KeyValuePair<string, string>(rd.Key, (string)rd.Value);
            }

            foreach (var qp in context.HttpContext.Request.QueryString)
            {
                yield return new KeyValuePair<string, string>((string)qp, context.HttpContext.Request.QueryString[(string)qp]);
            }
        }

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext == null) 
            {
                throw new ArgumentNullException("controllerContext");
            }
            if (bindingContext == null)
            {
                throw new ArgumentNullException("bindingContext");
            }

            Dictionary<K, V> dict = new Dictionary<K, V>();
            Dictionary<int, K> keys = new Dictionary<int, K>();
            Dictionary<int, V> values = new Dictionary<int, V>();

            foreach (KeyValuePair<string,string> formParam in GetValueProviderData(controllerContext))
            {
                if (formParam.Key.StartsWith(bindingContext.ModelName, StringComparison.InvariantCultureIgnoreCase))
                {
                    int startbracket = formParam.Key.IndexOf("[");
                    if (startbracket != bindingContext.ModelName.Length) throw new Exception("Did not find [ directly after model name");

                    int endbracket = formParam.Key.IndexOf("]", bindingContext.ModelName.Length + 1);
                    if (endbracket == -1) throw new Exception("Did not find closing bracket in " + formParam);

                    int idx;
                    string idxText = formParam.Key.Substring(bindingContext.ModelName.Length + 1, endbracket - bindingContext.ModelName.Length - 1);
                    if (!int.TryParse(idxText, out idx))
                    {
                        throw new Exception("Could not parse numeric index from " + formParam);
                    }

                    if (formParam.Key.EndsWith(".Key", StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (keys.ContainsKey(idx))
                        {
                            throw new Exception("The index " + idx + " was repeated.");
                        }

                        K dictKey;
                        try
                        {
                            dictKey = (K)Convert.ChangeType(formParam.Value, typeof(K));
                            keys.Add(idx, dictKey);
                        }
                        catch (NotSupportedException)
                        {
                            throw new Exception("The given key '" + formParam.Key + "' could not be converted to type K");
                        }
                    }
                    else if (formParam.Key.EndsWith(".Value", StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (values.ContainsKey(idx))
                        {
                            throw new Exception("The index " + idx + " was repeated.");
                        }

                        V dictValue;
                        try
                        {
                            dictValue = (V)Convert.ChangeType(formParam.Value, typeof(V));
                            values.Add(idx, dictValue);
                        }
                        catch (NotSupportedException)
                        {
                            throw new Exception("The given value '" + formParam.Value + "' could not be converted to type V");
                        }
                    }
                }
            }

            if (!keys.Keys.SequenceEqual(values.Keys))
            {
                throw new Exception("Keys and values do not match.");
            }

            foreach (KeyValuePair<int, K> kvp in keys)
            {
                dict.Add(kvp.Value, values[kvp.Key]);
            }

            return dict;
        }
    }
}

使用法:

public ActionResult _UpdateFilter(int a, string b, List<string> c, 
         [ModelBinder(typeof(OptionalDictionaryBinder<int, bool>))] 
         Dictionary<int, bool> sometimesSet)
于 2012-05-01T18:37:00.960 に答える