最終的に、MVCページの読み込みで参照されているのと同じ問題に対処しようとしています。「同じキーのアイテムが既に追加されています。」</a>というエラーが表示され、同じキーのアイテムがすでに追加されています。最初のリンクの複製は、すべてのMVCページが失敗し、同じキーを持つアイテムがすでに追加されているというメッセージが表示されますが、MVCページにのみ影響することを確認する追加の関連情報があり、Webフォームやその他のアプリケーションの側面が処理されますappSettingsを使用すると、エラーなしで引き続き機能します。
私はこれを本番環境で4回見ましたが、他の環境(開発、テスト、UAT)では見られませんでした。System.Web.WebPagesのソースコードとMVCスタックの関連セクションを詳しく調べてデバッグしましたが、目立ったものは見つかりませんでした。この問題はMVC3からMVC4への移行を通じて持続し、aspnetwebstack.codeplex.comからの最新のチェンジセットはこの問題に対処していないようです。
問題の簡単な要約:
- すべてのMVCページが影響を受け、完全に使用できなくなります
- appSettingsを使用するWebFormsおよびアプリケーションの他の側面は引き続き正常に機能します
- appPoolの再起動のみがこの問題を修正します
- 少なくとも1回、上記の記事で参照されているように、これはIISによるappPoolの定期的な時間間隔のリサイクル後に発生しました。
- これは、トラフィック量が少ない期間と多い期間の両方で発生しました
- これは複数の本番サーバーで発生しましたが、他のサーバーがMVCページを提供し続けるため、この問題は常に1台のサーバーにのみ影響します。
問題のあるコード行はですがvar items = new Lazy<Dictionary<object, object>>(() => appSettings.AllKeys.ToDictionary(key => key, key => (object)appSettings[key], comparer));
、appSettings変数からの値を要求することによって遅延初期化が強制された場合に発生しますitems
。これはSystem.Configuration.ConfigurationManager.AppSettingsへの直接静的参照であるSystem.Web.WebConfigurationManager.AppSettingsからのものです。だから私はその線が次のものと同等であると信じています:var items = new Lazy<Dictionary<object, object>>(() => System.Configuration.ConfigurationManager.AppSettings.AllKeys.ToDictionary(key => key, key => (object)appSettings[key], comparer));
フレームワークの問題を疑うことはめったにありませんが、appSettingsには同じ2つの異なるキーがあるようです(同じキーに対して複数の値をサポートするNameValueCollectionと同じではありません)。MVCスタックで使用されているのcomparer
はStringComparer.OrdinalIgnoreCaseであり、構成システムで使用されているものと一致しているように見えます。ToDictionary()
これがフレームワークの問題である場合、拡張メソッドを使用してNameValueCollecitonをディクショナリに強制すると、MVCスタックは非常に寛容ではないように見えます。を使用appSettings.AllKeys.Distinct().ToDictionary(...)
すると、MVCスタックがアプリケーションの他の部分と同じように正常に動作し、キーが重複する可能性を認識できなくなると思います。この容赦のない性質は、WebConfigScopeDictionaryのNullReferenceExceptionで説明されている問題の原因にもなっているようです。
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.<>c__DisplayClass4.<.ctor>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& 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.<>c__DisplayClass1a.<InvokeActionResultWithFilters>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.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult)
at System.Web.Mvc.Controller.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar)
at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
私の質問をすでに尋ねられた質問と区別するために、構成システムが複数の重複キーで破損しているのを見た人、またはNameValueCollection.AllKeysが2つの同一のキーを返す場所を見た人はいますか?構成ファイルで複数のキーを定義できることは知っていますが、最後のキーが優先され、そのシナリオではこの問題は再現されません。
この動作を何度も見ているのは私だけではありませんが、この問題を説明する投稿は比較的少ないので、構成/環境の問題である可能性もありますが、サーバーはこの問題が発生することなく数か月間実行され、 appPoolを再起動すると、すぐに問題が修正されます。
サーバーでこのエラーが発生し始めた場合にappPoolを強制的に再起動することでこの問題を軽減しましたが、一部のユーザーでもエラーが発生するため、管理者はこの「ハッキー」なソリューションに満足していません。
ヘルプ?!?!?
編集:
これは、シナリオを再現できるが、問題の解決には役立たない、工夫された安っぽいテストです。それは約の間に起こります。テストの20%が実行されます。コードは他のスレッドの理由で爆発しますが、重要なのは「同じキーがすでに追加されています」というエラーです。
[TestClass]
public class UnitTest1
{
readonly NameValueCollection _nameValueCollection = new NameValueCollection();
private Lazy<Dictionary<object, object>> _items;
[TestMethod]
public void ReproduceSameKeyHasAlreadyBeenAdded()
{
Thread[] threads = new Thread[1000];
for (int i = 0; i < 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<Dictionary<object, object>>(() => _nameValueCollection.AllKeys.ToDictionary(key => key, key => (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.<TestMethod1>b__0() in c:\Git\AspNetWebStack\aspnetwebstack\UnitTestProject1\UnitTest1.cs:line 37
at System.Lazy`1.CreateValue()