JsonValueProvider从MVC App中抛出错误

时间:2016-03-14 06:52:56

标签: asp.net asp.net-mvc

我们在日志中开始看到这个,知道从哪里开始寻找问题?调用堆栈中没有任何内容可以帮助我们开始。

    Message :An item with the same key has already been added. at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) at 
System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean 
add) at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) at 
System.Web.Mvc.JsonValueProviderFactory.EntryLimitedDictionary.Add(String key, 
Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary
backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary
backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at 
System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.ValueProviderFactoryCollection.<>c__DisplayClassc.<GetValueProvider>b__7(ValueProviderFactory factory) at 
System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at 
System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at 
System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at 
System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at 
System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.ControllerBase.get_ValueProvider() 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__DisplayClass8.<BeginProcessRequest>b__2(AsyncCallback asyncCallback, Object asyncState) at 
System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) 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)

2 个答案:

答案 0 :(得分:3)

当您的控制器方法收到json时,DefaultModelBinder使用JsonValueProviderFactoryJavaScriptSerializer使用DictionaryValueProvider反序列化json字符串,并将名称/值对添加到传递给的字典中{ name: someValue, NAME: anotherValue }反过来提供绑定值。

您的错误是由于您的json包含具有重复属性名称的对象引起的,例如name: someValue因此,一旦将NAME: anotherValue添加到字典中,添加ValueProvideFactory会引发异常,因为重复(不区分大小写)键。这是有道理的,因为模型不能包含2个具有相同名称的属性。

虽然最好的解决方案是跟踪并纠正'无效' json的来源,但您可以编写自己的yourProject.ValueProviderFactories来仅绑定第一个值并忽略后续值用同一把钥匙。要保存重新发明轮子,请使用以下步骤:

  1. JsonValueProviderFactory.cs source code的源代码复制到项目中
  2. 重命名命名空间(比如using System.Web.Mvc;)和 添加InvalidJsonValueProviderFactory声明
  3. 重命名班级(比如private class EntryLimitedDictionary
  4. 修改public void Add(string key, object value) { if (++_itemCount > _maximumDepth) { // throw new InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge); } // Add the following if block if (_innerDictionary.ContainsKey(key)) { return; } _innerDictionary.Add(key, value); }

    中的以下代码
    global.asax.cs
  5. 将以下内容添加到ValueProviderFactories.Factories.Insert(0, new yourProject.ValueProviderFactories.InvalidJsonValueProviderFactory()); 文件

    JsonValueProviderFactory
  6. 请注意,首先需要添加它,因为private int _index = 0; .... if (_innerDictionary.ContainsKey(key)) { string invalidKey = string.Format("duplicates[{0}]", _index++); string invalidValue = string.Format("{0}|{1}", key, value); _innerDictionary.Add(invalidKey, invalidValue); return; } 已添加到工厂集合中。

    这会阻止您的异常,但当然意味着重复属性将被丢弃。如果您重要的是重复,那么您可以考虑进行更多修改以创建具有包含索引器的不同唯一键的新词典条目,例如

    index

    其中string[] duplicates从零开始并且每次都递增。然后,您可以在方法{"propertyKeys":[{"name":"name","dataType":"String","cardinality":"SINGLE"},{"name":"languages","dataType":"String","cardinality":"SET"},{"name":"picture","dataType":"String","cardinality":"SINGLE"},{"name":"preferred_language","dataType":"String","cardinality":"SINGLE"},{"name":"bytes","dataType":"Integer","cardinality":"SINGLE"},{"name":"github_id","dataType":"String","cardinality":"SINGLE"},{"name":"twitter_id","dataType":"String","cardinality":"SINGLE"},{"name":"language_percentage","dataType":"Float","cardinality":"SINGLE"}],"vertexLabels":[{"name":"person"},{"name":"language"}],"edgeLabels":[{"name":"codes_in","multiplicity":"MULTI"},{"name":"used_by","multiplicity":"MULTI"}],"vertexIndexes":[{"name":"vByName","propertyKeys":["name"],"composite":true,"unique":false},{"name":"vByPreferredLang","propertyKeys":["preferred_language"],"composite":true,"unique":false},{"name":"vByLanguages","propertyKeys":["languages"],"composite":false,"unique":false}],"edgeIndexes":[{"name":"eByName","propertyKeys":["name"],"composite":true,"unique":false},{"name":"eByLanguagePercentage","propertyKeys":["language_percentage"],"composite":true,"unique":false}]} 中添加一个附加参数,该参数将填充重复值。至少它有助于调试问题的根源。

答案 1 :(得分:0)

我想问题是当你在Dictionary中插入新的相同键值时,那个时候你不会检查Key是否存在,所以当你插入已经存在的键时会发生这个问题。简单的解决方案是在插入之前只检查密钥是否存在。

// Create Dictionary with two key value pairs.
var dictionary = new Dictionary<string, int>()
{
    {"mac", 1000},
    {"windows", 500}
};

// Use ContainsKey method. To check Linux is exist or not
if (dictionary.ContainsKey("linux") == false)
{
    dictionary.Add("linux",300);         
} else
{
    Console.WriteLine("linux is already Exist");
}

在上面的例子中,mac,window和linux是目录的关键。目录有一个规则它永远不会再添加存在键。所以你必须在添加之前检查。