13

私はコントローラーを持っています:

private readonly ILogger _logger;    
private readonly IRepository _repository;

public HomeController(ILogger logger, IRepository repository)
{
   _logger = logger;
   _repository = repository;
}

これはリポジトリです:

public class EfRepository : IRepository
{
    // ...methods for add, delete, update entities
    // ....

    public void Dispose()
    {
         if (this._context != null)
         {
             this._context.SaveChanges();
             (this._context as IDisposable).Dispose();
             this._context = null;
         }
    }
}

最後に、IoC での登録の種類:

_builder.RegisterType<Logger>().As<ILogger>();
_builder.RegisterType<EfRepository>().As<IRepository>().WithParameter("context", new PcpContext());

アプリケーションを実行すると、次のエラーが表示されます。

DbContext が破棄されているため、操作を完了できません。

次のように登録 EfRepository を変更しようとしました。

_builder.RegisterType<EfRepository>()
   .As<IRepository>()
   .WithParameter("context", new PcpContext()).InstancePerLifetimeScope();

この場合、最初のリクエストは終了しますが、他のページを開こうとすると、再びエラーが発生します。問題はどこだ?

4

2 に答える 2

19

WithParameter メソッドを使用する場合、パラメータ インスタンスは、解決されたすべてのオブジェクトで同じになります。したがって、.WithParameter("context", new PcpContext())解決された IRepository のインスタンスに対して、PcpContext クラスの同じインスタンスを効果的に使用しています。

現在のコードでは、IRepository インスタンスが破棄されると、その PcpContext インスタンスも破棄されます。その後、IRepository を解決しようとすると、破棄された PcpContext インスタンスが返されます。リクエストの最後に破棄される各 Http リクエストで、EF DbContext の新しいインスタンスを受け取る方法が必要です。

IRepository の解決が必要になるたびにコード ブロックが実行されるように、IRepository のコード ブロックを登録することもできます。

_builder.Register<IRepository>(c => new EfRepository(new PcpContext()))

より良いオプションは、新しい抽象化を作成し、クラスではなく新しい IDatabaseContext 抽象化に依存するようにIDatabaseContext更新することです(これはすでに当てはまる場合があります:))。EfRepositoryPcpContext

IDatabaseContext の実装クラスは、EF DbContext から継承し、おそらく接続文字列をパラメーターとして受け取る必要がある PcpContext クラスになります。

public class EfRepository : IRepository
{
    private readonly IDatabaseContext _context;

    public EfRepository(IDatabaseContext context)
    {
        _context = context;
    }

    ...methods for add, delete, update entities

    //There is no longer need for this to be disposable.
    //The disaposable object is the database context, and Autofac will take care of it
    //public void Dispose()
}

public interface IDatabaseContext : IDisposable 
{
    ... declare methods for add, delete, update entities
}

public class PcpContext: DbContext, IDatabaseContext 
{
    public EntityFrameworkContext(string connectionString)
        : base(connectionString)
    {
    }

    ...methods exposing EF for add, delete, update entities

    //No need to implement IDisposable as we inherit from DbContext 
    //that already implements it and we don´t introduce new resources that should be disposed of
}

これは、IoC コンテナーを使用し、ライフタイム管理の負担をそれらに任せるというアイデアで改善されます。これで、Repository クラスを使い捨てにする必要も、その IDatabaseContext 依存関係を管理および破棄する必要もなくなりました。コンテキスト インスタンスを追跡し、必要に応じて破棄するのは Autofac です。

同じ理由で、データベース コンテキストの依存関係で InstancePerLifetimeScope を使用することをお勧めします。これは、同じ EF コンテキストが同じ Http 要求のすべてのリポジトリ インスタンスで共有され、要求の最後に破棄されることを意味します。

_builder.RegisterType<EfRepository>()
   .As<IRepository>();

_builder.RegisterType<PcpContext>()
   .As<IDatabaseContext>()
   .WithParameter("connectionString", "NameOfConnStringInWebConfig")
   .InstancePerLifetimeScope();
于 2013-02-17T11:27:43.150 に答える
0

@Daniel JGが提案したように、「コードブロック」の簡単なソリューション(ラムダ)を使用しました。

Autofac でのこのコード例を以下に示します。Daniels の例は Unity であり、彼は自分自身についても言及しています。OPがAutofacをタグとして追加したため、これは私に関連しているように見えました:

_builder.Register(c => new AppDbContext()).As(typeof(AppDbContext));

そのコードDbContext has been disposedは、Entity Framework で抱えていた問題を解決しました。Unity を含む他のほとんどの DI コンテナーと比較して、Autofac は登録されたものとそれが解決されるものを切り替えることに注意してください。

OPによって与えられたコード例の場合、修正は次のようになります。

_builder.Register(c => new EfRepository(new PcpContext())).As(IRepository);

この最後のビットはテストされていないコードであることに注意してください。ただし、詳細については、ダニエルズの回答を参照する必要があります。彼は「より良いオプション」で正しいと思うからです。しかし、(私のように) 今時間がない場合は、私のソリューション オプションを使用することもできます。TODO を追加するだけで、発生している技術的負債をうまく処理できます :)。

私がそうするとき、彼の「より良いオプション」に従うAutofacの作業コードでこの回答を更新できるかどうかを確認します。まず、この記事をよく読みたいと思います。ざっと読んだところ、Autofac の人々はライフタイム スコープを処理するために「Service Locator」を使用することを推進しているように思えます。しかし、Mark Seemann によると、それはアンチ パターンなので、把握しなければならないことがいくつかあります...意見のある DI 専門家はいますか?

于 2015-11-20T16:04:56.670 に答える