5

Entity Framwork、SignalR、および Hangfire ジョブを使用する ASP.NET MVC プロジェクトがあります。

私のメイン (ルート) コンテナーは次のように定義されています。

builder.RegisterType<DbContext>().InstancePerLifetimeScope(); // EF Db Context
builder.RegisterType<ChatService>().As<IChatService>().SingleInstance(); // classic "service", has dependency on DbContext
builder.RegisterType<ChatHub>().ExternallyOwned(); // SignalR hub
builder.RegisterType<UpdateStatusesJob>().InstancePerDependency(); // Hangfire job
builder.RegisterType<HomeController>().InstancePerRequest(); // ASP.NET MVC controller
IContainer container = builder.Build();

MVC の場合、Autofac.MVC5 nuget パッケージを使用しています。依存関係リゾルバー:

DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

SignalR の場合、Autofac.SignalR nuget パッケージを使用しています。依存関係リゾルバー:

GlobalHost.DependencyResolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);

私の signalR ハブは次のようにインスタンス化されます ( http://autofac.readthedocs.org/en/latest/integration/signalr.html#managing-dependency-lifetimes ):

private ILifetimeScope _hubScope;
protected IChatService ChatService;
public ChatHub(ILifetimeScope scope) {
  _hubScope = scope.BeginLifetimeScope(); // scope 
  ChatService = _hubScope.Resolve<IChatService>(); // this service is used in hub methods
}
protected override void Dispose(bool disposing)
{
  // Dipose the hub lifetime scope when the hub is disposed.
  if (disposing && _hubScope != null)
  {
    _hubScope.Dispose();
  }
  base.Dispose(disposing);
}

Hangfire の場合、Hangfire.Autofac パッケージを使用しています。

config.UseActivator(new AutofacJobActivator(container));

ジョブは次のようにインスタンス化されます。

private readonly ILifetimeScope _jobScope;
protected IChatService ChatService;
protected BaseJob(ILifetimeScope scope)
{
    _jobScope = scope.BeginLifetimeScope();
    ChatService = _jobScope.Resolve<IChatService>();
}
public void Dispose()
{
    _jobScope.Dispose();
}

質問/問題: ハブとジョブで常に DbContext の同じインスタンスを取得します。すべてのハブ インスタンスが同じ ChatService を取得するようにしたいのですが、DbContext (ChatService の依存関係) は常に新しいインスタンスになります。また、Hangfire ジョブも同じように動作するはずです。

これを行うことができますか、それとも何か不足していますか?

更新 1:

考えた(そして寝た)後、私には2つの選択肢があると思います。「リクエストごとのセッション」(「ハブごとのセッション」、「ジョブごとのセッション」) を維持したいと考えています。

オプション1:

すべてのサービスが InstancePerLifetimeScope を持つように変更します。サービスのインスタンス化は高価ではありません。ある種の状態を維持するサービスの場合、SingleInstance でセッション (DbContext) に依存しない別の「ストレージ」(クラス) を作成します。これは、ハブやジョブでも機能すると思います。

オプション 2:

@Ric .Net によって提案されたある種のファクトリを作成します。このようなもの:

public class DbFactory: IDbFactory
{
    public MyDbContext GetDb()
    {
        if (HttpContext.Current != null)
        {
            var db = HttpContext.Current.Items["db"] as MyDbContext;
            if (db == null)
            {
                db = new MyDbContext();
                HttpContext.Current.Items["db"] = db;
            }
            return db;
        }

        // What to do for jobs and hubs?
        return new MyDbContext();
    }
}

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        var db = HttpContext.Current.Items["db"] as MyDbContext;
        if (db != null)
        {
            db.Dispose();
        }
    }

これはMVCで機能すると思いますが、ハブ(すべてのハブ呼び出しはハブの新しいインスタンスです)とジョブ(ジョブのすべての実行はジョブクラスの新しいインスタンスです)で機能させるにはどうすればよいかわかりません.

私は選択肢 1 に傾いています。どう思いますか?

どうもありがとう!

4

2 に答える 2

4

私はAutoFacの経験がまったくありません。しかし、私の注意を引いたのは:

すべてのハブ インスタンスが同じ ChatService を取得するようにしたいのですが、DbContext (ChatService の依存関係) は常に新しいインスタンスになります。

ここで基本的に言っていることは次のとおりです。

「私の車はガレージに依存している同じ自動車会社によってメンテナンスされていますが、車を持ち込むたびにガレージを新しいものにしたいと思っています。」

(依存関係を含む完全にビルドされたインスタンス)を他のコンポーネントに注入するChatServiceと、もちろん、他の種類のライフスタイルがあるかどうかに関係なく、他の依存関係もビルドされます。注入されたオブジェクトよりも寿命が短いオブジェクトが作成されると、いわゆる「キャプティブ依存関係」が作成されます

の新しい「インスタンス」を取得する唯一の方法は、それ自体を注入するのDbContextChatServiceはなく、使用するたびにを作成する を注入することです。DbContextDbContextFactoryDbContext

実装は次のようになります。

public class DbContextFactory
{
    public DbContext Create()
    {
         return new DbContext();
    }
}

//usage:
public class ChatService
{
     private readonly DbContextFactory dbContextFactory;

     public ChatService(DbContextFactory dbContextFactory)
     {
         this.dbContextFactory = dbContextFactory;
     }

    public void SomeMethodInChatService()
    {
         using (var db = this.dbContextFactory.Create())
         {
             //do something with DbContext    
         }
     }
}

Singleton Lifestyle を使用してAutoFacDbContextFactoryに登録できます。

しかし、これはあなたが目指しているものではないかもしれません。この場合、使用するたびDbContextに新しいものを取得するためです。一方、ここで読むことができるように、新しい DbContext はおそらくこれにアプローチする最も安全な方法です。

この素晴らしい答えは、状況に非常に適したコマンド/ハンドラーパターンの使用方法の説明があるため、複数の理由で読む価値があります。

これにより、チャットサービスは を完全に認識できなくなります。これにより、アプリケーションの ' SOLID ' 設計がDbContext改善され、 orを直接注入すると実質的に元に戻せるものであるをテストする可能性が生まれます。ChatServiceDbContextDbContextFactory

于 2015-03-16T20:55:15.453 に答える
1

工場を解決する必要があります。Autofacには、動的インスタンス化などのサポートが組み込まれていFunc<T>ます。

依存関係に破棄可能な依存関係がある場合、メモリ リークを回避するために破棄パターンを管理する必要があります。Autofacを使用してこれを解決する一般的なパターンは、Func<Owned<T>>

public class ChatService
{
    public ChatService(Func<Owned<DbContext>> dbContextFactory)
    {
        this._dbContextFactory = dbContextFactory;
    }

    private readonly Func<Owned<DbContext>> _dbContextFactory;

    private void DoSomething()
    {
        using (Owned<DbContext> ownedDbContext = this._dbContextFactory())
        {
            DbContext context = ownedDbContext.Value;
        }
    }
}

Func<T>工場です。ファクトリが呼び出されるたびに、autofac は新しいインスタンスを返します (登録の有効期間がどのように構成されているかによって異なります)。 Owned<T>このクラスの主なILifetimescope目的は、解決されたコンポーネントの破棄を管理することです。

Func<Owned<T>>ここで詳細情報見つけることができます:Owned<T>Func<T>

于 2015-03-16T22:12:10.807 に答える