45

数日前、ASP.Net のスレッド化で問題が発生しました。Web リクエストごとにシングルトン オブジェクトが必要でした。私は実際に私の仕事の単位にこれが必要です。ID マップがリクエスト全体で有効になるように、Web リクエストごとに作業単位をインスタンス化したかったのです。このようにして、IoC を使用して独自の IUnitOfWork をリポジトリ クラスに透過的に注入し、同じインスタンスを使用してエンティティをクエリしてから更新することができます。

Unityを使っているのでPerThreadLifeTimeManagerを間違えて使ってしまいました。ASP.Net スレッド モデルは、私が達成したいことをサポートしていないことにすぐに気付きました。基本的に、スレッドプールを使用してスレッドをリサイクルします。つまり、スレッドごとに 1 つの UnitOfWork を取得します!! ただし、私が望んでいたのは、Web リクエストごとに 1 つの作業単位でした。

少しグーグルで調べたところ、この素晴らしい投稿がありました。それがまさに私が望んでいたことです。達成するのが非常に簡単だった団結の部分を除いて.

これは、統一のための 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());

誰かの時間を少し節約できることを願っています。

4

3 に答える 3

25

きちんとした解決策ですが、 LifetimeManager の各インスタンスは、定数ではなく一意のキーを使用する必要があります。

private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid());

そうしないと、PerCallContextLifeTimeManager に複数のオブジェクトが登録されている場合、それらは CallContext にアクセスするために同じキーを共有しているため、期待したオブジェクトが返されません。

オブジェクトが確実にクリーンアップされるように、RemoveValue を実装する価値もあります。

public override void RemoveValue()
{
     CallContext.FreeNamedDataSlot(_key);
}
于 2010-06-10T11:20:50.807 に答える
21

この 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

于 2011-01-15T05:11:54.533 に答える
4

あなたの貢献に感謝します,

しかし、質問提案の実装には 2 つの欠点があります。そのうちの 1 つは、Steven Robbins が回答で、Micah Zoltuがコメントですでに述べているように、深刻なバグです。

  1. 呼び出しコンテキストは、単一の要求に対して Asp.Net によって保持されるとは限りません。負荷がかかると、別のものに切り替わり、提案された実装が壊れる可能性があります。
  2. リクエスト終了時の依存関係の解放は処理しません。

現在、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。モジュールの実行順序に依存します。

于 2015-04-03T09:38:10.053 に答える