JsonValueProvider throwing error from MVC App

We started seeing this in our log, any idea where to start looking for the problem? There is nothing in the call stack to help us get started.

    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)

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

When your controller method receives json, the DefaultModelBinder uses the JsonValueProviderFactory, which uses JavaScriptSerializer to deserialize the json string and adds the name/value pairs to a dictionary which is passed to the DictionaryValueProvider which in turn provides the values for binding.

Your error is caused because your json contains an object with duplicate property names, for example { name: someValue, NAME: anotherValue } so once name: someValue has been added to the dictionary, adding NAME: anotherValue throws an exception because of the duplicate (case-insensitive) key. This makes sense because a model cannot include 2 properties with the same name.

While the best solution will be to track down and correct the source of the ‘invalid’ json, you can write your own ValueProvideFactory to bind only the first value and ignore subsequent values with the same key. To save reinventing the wheel use the following steps:

  1. Copy the source code of the JsonValueProviderFactory.cs source
    code
    into your project
  2. Rename the namespace (say yourProject.ValueProviderFactories) and
    add a using System.Web.Mvc; statement
  3. Rename the class (say InvalidJsonValueProviderFactory)
  4. Modify the following code inside the private class EntryLimitedDictionary
    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);
    }
  5. Add the following to your global.asax.cs file
    ValueProviderFactories.Factories.Insert(0, new yourProject.ValueProviderFactories.InvalidJsonValueProviderFactory());

Note that it needs to be added first because the JsonValueProviderFactory has already been added to the collection of factories.

This will prevent your exception, but of course means the duplicate property will be throw away. If its important that you get that duplicate, then you could consider more modifications to create a new dictionary entry with a different unique key containing indexers, for example

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

where index would start at zero and be incremented each time. Then you could add an additional parameter in the method string[] duplicates which would be populated with the duplicate values. At the very least it would aid in debugging the source of the problem.

Method 2

I guess issue is when you insert new same key value in Dictionary, that time you are not going to check Key is exist or not, so when you insert already exist key this issue will occur. Simple solution is just check key exist or not before going to insert.

// 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");
}

In Above Example mac,window and linux are key of directory. directory have one rule it will never add again exist key. so you have to check before add.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x