title: All MVC pages fail with the message an item with the same key has already been added
body: <p>Ultimately I am trying to address the same issue that is referenced in <a href="https://stackoverflow.com/questions/13321205/loading-any-mvc-page-fails-with-the-error-an-item-with-the-same-key-has-already">Loading any MVC page fails with the error “An item with the same key has already been added.”</a> and <a href="http://forums.asp.net/t/1843922.aspx/1?+An+item+with+the+same+key+has+already+been+added+exception+thrown+fixed+by+recycling+app+pool" rel="nofollow noreferrer">An item with the same key has already been added</a>. A duplicate of the first link is <a href="http://dotnetforum.net/topic/62125-all-mvc-pages-fail-with-the-message-an-item-with-the-same-key-has-already-been-added" rel="nofollow noreferrer">All MVC pages fail with the message an item with the same key has already been added</a> but it has some additional pertinent information, confirming that it only effects MVC pages, while webforms and other aspects of the application that deal with appSettings continue to work without error.</p>
  
  <p>I have now seen this four times in production and it has not been seen in any other environment (dev, test, UAT). I have closely examined and debugged through the source code of System.Web.WebPages and the relevant sections of the MVC stack but did not run into anything that stood out.  This problem has persisted through a migration from MVC3 to MVC4, and the latest changeset from aspnetwebstack.codeplex.com does not appear to address this issue.</p>
  
  <p>A quick summary of the issue:</p>
  
  <ul>
  <li>Every MVC page is affected and completely unusable</li>
  <li>WebForms and other aspects of the application that use appSettings continue to work just fine</li>
  <li>Only an appPool restart will correct this issue</li>
  <li>At least once and as referenced in an article above, this has happened after a regular time interval recycle of the appPool by IIS</li>
  <li>This has happened during both low and high volume traffic periods</li>
  <li>This has happened on multiple production servers, but the issue only affects a single server at any given time, as other servers continue serving MVC pages</li>
  </ul>
  
  <p>The offending line of code is <code>var items = new Lazy&lt;Dictionary&lt;object, object&gt;&gt;(() =&gt; appSettings.AllKeys.ToDictionary(key =&gt; key, key =&gt; (object)appSettings[key], comparer));</code>, but it occurs when the lazy initialization is forced by requesting a value from <code>items</code>  The appSettings variable is from System.Web.WebConfigurationManager.AppSettings which is a direct static reference to System.Configuration.ConfigurationManager.AppSettings. So I beleive the line is equivalent to: <code>var items = new Lazy&lt;Dictionary&lt;object, object&gt;&gt;(() =&gt; System.Configuration.ConfigurationManager.AppSettings.AllKeys.ToDictionary(key =&gt; key, key =&gt; (object)appSettings[key], comparer));</code></p>
  
  <p>I rarely suspect framework issues, but it appears that appSettings has two distinct keys that are the same (not the same as a NameValueCollection supporting multiple values for the same key).  The <code>comparer</code> being used in the MVC stack is the StringComparer.OrdinalIgnoreCase which appears to match what is used by the configuration system. If this is a framework issue, the MVC stack appears to be very unforgiving when it forces the NameValueColleciton into a dictionary using the <code>ToDictionary()</code> extension method.  I believe using <code>appSettings.AllKeys.Distinct().ToDictionary(...)</code> would probably allow the MVC stack to operate normally as the rest of the application does and be oblivious to the possibility of duplicate keys. This unforgiving nature appears to also contribute to the issue described in <a href="https://stackoverflow.com/questions/14577735/nullreferenceexception-in-webconfigscopedictionary">NullReferenceException in WebConfigScopeDictionary</a> </p>
  
  <pre><code>Server stack trace: 
     at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
     at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
     at System.Web.WebPages.Scope.WebConfigScopeDictionary.&lt;&gt;c__DisplayClass4.&lt;.ctor&gt;b__0()
     at System.Lazy`1.CreateValue()
  Exception rethrown at [0]: 
     at System.Lazy`1.get_Value()
     at System.Web.WebPages.Scope.WebConfigScopeDictionary.TryGetValue(Object key, Object&amp; value)
     at System.Web.Mvc.ViewContext.ScopeGet[TValue](IDictionary`2 scope, String name, TValue defaultValue)
     at System.Web.Mvc.ViewContext.ScopeCache..ctor(IDictionary`2 scope)
     at System.Web.Mvc.ViewContext.ScopeCache.Get(IDictionary`2 scope, HttpContextBase httpContext)
     at System.Web.Mvc.ViewContext.GetClientValidationEnabled(IDictionary`2 scope, HttpContextBase httpContext)
     at System.Web.Mvc.Html.FormExtensions.FormHelper(HtmlHelper htmlHelper, String formAction, FormMethod method, IDictionary`2 htmlAttributes)
     at ASP._Page_Areas_Client_Views_Equipment_Index_cshtml.Execute()
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
     at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
     at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
     at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
     at System.Web.Mvc.ControllerActionInvoker.&lt;&gt;c__DisplayClass1a.&lt;InvokeActionResultWithFilters&gt;b__17()
     at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
     at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
     at System.Web.Mvc.Async.AsyncControllerActionInvoker.&lt;&gt;c__DisplayClass25.&lt;BeginInvokeAction&gt;b__22(IAsyncResult asyncResult)
     at System.Web.Mvc.Controller.&lt;&gt;c__DisplayClass1d.&lt;BeginExecuteCore&gt;b__18(IAsyncResult asyncResult)
     at System.Web.Mvc.Async.AsyncResultWrapper.&lt;&gt;c__DisplayClass4.&lt;MakeVoidDelegate&gt;b__3(IAsyncResult ar)
     at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
     at System.Web.Mvc.Async.AsyncResultWrapper.&lt;&gt;c__DisplayClass4.&lt;MakeVoidDelegate&gt;b__3(IAsyncResult ar)
     at System.Web.Mvc.MvcHandler.&lt;&gt;c__DisplayClass8.&lt;BeginProcessRequest&gt;b__3(IAsyncResult asyncResult)
     at System.Web.Mvc.Async.AsyncResultWrapper.&lt;&gt;c__DisplayClass4.&lt;MakeVoidDelegate&gt;b__3(IAsyncResult ar)
     at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
     at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously)
  </code></pre>
  
  <p>To separate my question from the questions already asked, I will ask has anyone seen the configuration system be corrupted with multiple duplicate keys or where a NameValueCollection.AllKeys returns two identical keys? I know you can have multiple keys defined in the config file, but the last key wins, and that scenario does not reproduce this issue.</p>
  
  <p>Although I am not alone in seeing this behavior multiple times, there are relatively few posts describing this issue, so I also suspect that it might be a configuration/environmental issue, but again, servers will run for months without experiencing this issue, and an appPool restart immediately corrects the problem.</p>
  
  <p>I have mitigated this issue by forcing an appPool restart if a server starts seeing this error, but management is not happy about this "hacky" solution because some user will still experience an error.</p>
  
  <p>Help?!?!?</p>
  
  <p>EDIT:</p>
  
  <p>Here is a contrived, cheezy test that can reproduce the scenario, but doesn't help in solving the issue.  It happens during approx. 20% of the test runs. The code will blow up for other threading reasons, but it is the "Same key has already been added" error that is of interest.</p>
  
  <pre><code>[TestClass]
  public class UnitTest1
  {
      readonly NameValueCollection _nameValueCollection = new NameValueCollection();
      private Lazy&lt;Dictionary&lt;object, object&gt;&gt; _items;
  
      [TestMethod]
      public void ReproduceSameKeyHasAlreadyBeenAdded()
      {
          Thread[] threads = new Thread[1000];
          for (int i = 0; i &lt; 1000; i++)
          {
              ThreadStart threadStart = AddEntry;
              Thread thread = new Thread(threadStart);
              threads[i] = thread;
          }
          foreach (var thread in threads)
              thread.Start();
          Thread.Sleep(100);
          _items = new Lazy&lt;Dictionary&lt;object, object&gt;&gt;(() =&gt; _nameValueCollection.AllKeys.ToDictionary(key =&gt; key, key =&gt; (object)_nameValueCollection[key], ScopeStorageComparer.Instance));
  
          object value;
          _items.Value.TryGetValue("4", out value); // approx. 20% of time, blows up here with mentioned error
          Assert.IsTrue(value != null);
      }
  
      private int _counter;
      private void AddEntry()
      {
          _counter++;
          try
          { // add a bunch of even keys, every other one a duplicate
              _nameValueCollection.Add((_counter%2) == 0 ? _counter.ToString() : (_counter + 1).ToString(), "some value");
          }
          catch {}
      }
  }
  
  
  StackTrace:
         at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
         at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
         at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
         at UnitTestProject1.ReproduceSameKeyHasAlreadyBeenAdded.&lt;TestMethod1&gt;b__0() in c:\Git\AspNetWebStack\aspnetwebstack\UnitTestProject1\UnitTest1.cs:line 37
         at System.Lazy`1.CreateValue()
  </code></pre>