Unity の呼び出しコンテキストごとのシングルトン (Web リクエスト)
-
18-09-2019 - |
質問
数日前、ASP.Net スレッドで問題が発生しました。Webリクエストごとにシングルトンオブジェクトが必要でした。実際、私の仕事単位でこれが必要です。Web リクエストごとに作業単位をインスタンス化して、アイデンティティ マップがリクエスト全体で有効になるようにしたいと考えていました。この方法では、IoC を使用して独自の IUnitOfWork をリポジトリ クラスに透過的に挿入でき、同じインスタンスを使用してクエリを実行し、エンティティを更新できます。
Unityを使用しているため、間違ってPerThreadLifeTimeManagerを使用してしまいました。ASP.Net スレッド モデルは私が達成したいことをサポートしていないことにすぐに気づきました。基本的にはスレッドプールを使用し、スレッドをリサイクルします。つまり、スレッドごとに 1 つの UnitOfWork を取得することになります。ただし、私が望んでいたのは、Web リクエストごとに 1 つの作業単位でした。
少しグーグルで調べてみたところ、 この素晴らしい投稿. 。それはまさに私が望んでいたものでした。統一の部分を除いては、非常に簡単に達成できました。
これは Unity 用の PerCallContextLifeTimeManager の実装です。
public class PerCallContextLifeTimeManager : LifetimeManager
{
private const string Key = "SingletonPerCallContext";
public override object GetValue()
{
return CallContext.GetData(Key);
}
public override void SetValue(object newValue)
{
CallContext.SetData(Key, newValue);
}
public override void RemoveValue()
{
}
}
そしてもちろん、これを使用して、次のようなコードで作業単位を登録します。
unityContainer
.RegisterType<IUnitOfWork, MyDataContext>(
new PerCallContextLifeTimeManager(),
new InjectionConstructor());
誰かの時間を少しでも節約できることを願っています。
解決
ニート溶液が、一定ではなく、一意のキーを使用する必要がLifetimeManagerの各インスタンス
private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid());
あなたはPerCallContextLifeTimeManagerに登録し、複数のオブジェクトが、彼らはCallContextにアクセスするために同じ鍵を共有している、とあなたが期待されるオブジェクトを取り戻すません持っているそれ以外の場合ています。
:オブジェクトがクリーンアップされていることを確認したremoveValueを実装するまた価値があります
public override void RemoveValue()
{
CallContext.FreeNamedDataSlot(_key);
}
他のヒント
これはこのPerCallContextLifeTimeManager罰金呼び出しですが、私はこれはかなり確信しているのないのASP.Netごとの要求LifeTimeManagerと見なされるために「安全」。
ASP.Netは、そのスレッド・スワップを行う場合は、その後、CallContextを通じて新しいスレッドに渡って撮影した、の唯一の事は、現在のHttpContextです - あなたはCallContextに格納何かが消えてしまいます。これは、上記のコードは予期しない結果を持っている可能性が高負荷を意味します - !と私はダウンして追跡する本当の痛みだろうと想像する理由
これを実行するだけで「安全」な方法はHttpContext.Current.Itemsである、またはのようなものをやってます:
public class PerCallContextOrRequestLifeTimeManager : LifetimeManager
{
private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid());
public override object GetValue()
{
if(HttpContext.Current != null)
return GetFromHttpContext();
else
return GetFromCallContext();
}
public override void SetValue(object newValue)
{
if(HttpContext.Current != null)
return SetInHttpContext();
else
return SetInCallContext();
}
public override void RemoveValue()
{
}
}
これは明らかにSystem.Webの依存関係を取る意味: - (
でご利用いただけます。この上のより多くの情報:
http://piers7.blogspot.com/2005/11/ threadstatic-callcontext-and_02.htmlする
ご貢献いただきありがとうございます。
しかし、提案された実装には 2 つの欠点があり、そのうちの 1 つは Steven Robbins が既に述べたように重大なバグです。 彼の答え そしてミカ・ゾルトゥ コメントで.
- 呼び出しコンテキストは、単一の要求に対して Asp.Net によって保持されることは保証されていません。負荷がかかると、別のものに切り替わり、提案された実装が中断される可能性があります。
- リクエスト終了時の依存関係の解放は処理しません。
現在、Unity.Mvc Nuget パッケージは PerRequestLifetimeManager
仕事をするために。関連するものを登録することを忘れないでください UnityPerRequestHttpModule
ブートストラップ コード内でそうしないと、依存関係の解放も処理されません。
ブートストラップの使用
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
または web.config 内で system.webServer/modules
<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" />
現在の実装は Web フォームにも適しているようです。また、MVC にも依存しません。残念ながら、そのアセンブリには他のクラスが含まれているため、アセンブリは機能します。
解決された依存関係を使用してカスタム http モジュールを使用する場合、それらはすでにモジュール内で破棄されている可能性があることに注意してください。 EndRequest
. 。モジュールの実行順序によって異なります。