Asp.net mvc 3 MVC 3控制器操作中的可选字典参数
我有一个MVC 3控制器动作定义如下:Asp.net mvc 3 MVC 3控制器操作中的可选字典参数,asp.net-mvc-3,Asp.net Mvc 3,我有一个MVC 3控制器动作定义如下: public ActionResult _UpdateFilter(int a, string b, List<string> c) <input type="hidden" id="id-@(something)-Key" name="sometimesSet[@(idx)].Key" value="@(myIntValue)" /> <input type="hidden" id="id-@(something)-Val
public ActionResult _UpdateFilter(int a, string b, List<string> c)
<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)
(问题末尾的长堆栈跟踪)
问题
我收集的路由映射无法确定SomeTimeSet
是可选的,但我不知道如何正确配置它。我该怎么做?我没有在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)
at System.Collections.Generic.Dictionary`2.FindEntry(TKey)
at System.Collections.Generic.Dictionary`2.ContainsKey(TKey)
位于System.Web.Mvc.ValueProviderUtil.GetKeysFromPrefix(IEnumerable`1集合,字符串前缀)
位于System.Web.Mvc.DictionaryValueProvider`1.GetKeysFromPrefix(字符串前缀)
位于System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefixFromProvider(IValueProvider provider,字符串前缀)
在System.Web.Mvc.ValueProviderCollection.c__DisplayClass11.b_;c(IValueProvider provider)
在System.Linq.Enumerable.WhereSelectEnumerableInterator`2.MoveNext()中
在System.Linq.Enumerable.WhereSelectEnumerableInterator`2.MoveNext()中
位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)
位于System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefix(字符串前缀)
位于System.Web.Mvc.DefaultModelBinder.UpdateDictionary(ControllerContext ControllerContext,ModelBindingContext bindingContext,Type keyType,Type valueType)
位于System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext ControllerContext,ModelBindingContext bindingContext)
位于System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext ControllerContext,ModelBindingContext bindingContext)
位于System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext ControllerContext,ParameterDescriptor ParameterDescriptor)
位于System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext ControllerContext,ActionDescriptor ActionDescriptor)
在System.Web.Mvc.Async.AsyncControllerActionInvoker.c_uuDisplayClass25.b_u1E(AsyncCallback AsyncCallback,Object asyncState)
位于System.Web.Mvc.Async.AsyncResultRapper.WrappedAsyncResult`1.Begin(异步回调、对象状态、Int32超时)
位于System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext ControllerContext,String actionName,AsyncCallback回调,对象状态)
在System.Web.Mvc.Controller.c__displayClassId.b_uu 17(AsyncCallback AsyncCallback,Object asyncState)
位于System.Web.Mvc.Async.AsyncResultRapper.WrappedAsyncResult`1.Begin(异步回调、对象状态、Int32超时)
位于System.Web.Mvc.Controller.BeginExecuteCore(异步回调,对象状态)
位于System.Web.Mvc.Async.AsyncResultRapper.WrappedAsyncResult`1.Begin(异步回调、对象状态、Int32超时)
位于System.Web.Mvc.Controller.BeginExecute(RequestContext RequestContext、AsyncCallback回调、对象状态)
位于System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext RequestContext、AsyncCallback回调、对象状态)
在System.Web.Mvc.MvcHandler.c__DisplayClass6.c__DisplayClassb.b__3(AsyncCallback AsyncCallback,Object asyncState)
位于System.Web.Mvc.Async.AsyncResultRapper.WrappedAsyncResult`1.Begin(异步回调、对象状态、Int32超时)
在System.Web.Mvc.MvcHandler.c_uuudisplayClass6.b_uuu2()中
在System.Web.Mvc.SecurityUtil.c__DisplayClassb`1.b__a()中
在System.Web.Mvc.SecurityUtil.b_0(操作f)
位于System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(操作)
位于System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust[TResult](Func`1 Func)
位于System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext、AsyncCallback回调、对象状态)
位于System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext HttpContext,AsyncCallback回调,对象状态)
位于System.Web.Mvc.MvcHandler.System.Web.IHTTPassynchandler.BeginProcessRequest(HttpContext上下文、AsyncCallback cb、Object extraData)
在System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()中
在System.Web.HttpApplication.ExecuteStep(IExecutionStep步骤,布尔值&同步完成)
我发现最干净的解决方案是实现我自己的ModelBinder,它了解如何填充字典
,即使在线路上找不到相关值(它将返回一个空的字典
)
这是密码
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;
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Web;
使用System.Web.Mvc;
命名空间My.ModelBinders
{
///
///使用中概述的基本格式绑定到通用词典
/// http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
///通过放松,指数可以是任意整数(不需要从0开始,也不需要是连续的)。
///返回一个空字典,其中没有在连接上找到匹配的参数,而不是抛出
///与当前默认活页夹相同的异常。
///
///键类型
///值类型
公共类可选字典绑定器:CustomBinderBase、IModelBinder
{
///
///从请求中拉出键/值对。修改自
/// https://github.com/loune/MVCStuff/blob/master/Extensions/DefaultDictionaryBinder.cs
///当前不支持的文件。
///
///
///
私有IEnumerable GetValueProviderData(ControllerContext上下文)
{
foreach(context.HttpContext.Request.Form.Keys中的var fk)
{
返回新的KeyValuePair((string)fk,context.HttpContext.Request.Form[(string)fk]);
}
foreach(context.RouteData.Values中的var rd)
{
返回新的KeyValuePair(rd.Key,(string)rd.Value);
}
foreach(context.HttpContext.Request.QueryString中的var qp)
{
返回新的KeyValuePair((字符串)qp,context.HttpContext.R
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)