簡體   English   中英

DbContext已經處理掉並且是autofac

[英]DbContext has been disposed and autofac

我有一個控制器:

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();

在這種情況下,第一個請求完成,但在嘗試打開其他頁面時,我再次收到錯誤。 問題出在哪兒?

使用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抽象,更新EfRepository所以這取決於新IDatabaseContext抽象,而不是PcpContext類(可能已經是這樣的:))。

IDatabaseContext的實現類將是您的PcpContext類,它必須從EF DbContext繼承,並且可能接收連接字符串作為參數。

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與數據庫上下文相關性一起使用。 這意味着在同一個Http請求上為每個存儲庫實例共享相同的EF上下文,並在請求結束時進行處理。

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

_builder.RegisterType<PcpContext>()
   .As<IDatabaseContext>()
   .WithParameter("connectionString", "NameOfConnStringInWebConfig")
   .InstancePerLifetimeScope();

我使用@Daniel JG建議的“代碼塊”的簡單解決方案(lambda)。

下面是Autofac中的代碼示例。 丹尼爾斯的例子是Unity,因為他也提到了自己。 因為OP將Autofac添加為標簽,這似乎與我相關:

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

該代碼解決了DbContext has been disposed我與Entity Framework有關的問題。 請注意,與大多數其他DI容器(包括Unity)相比,Autofac會在已注冊的東西周圍進行切換,並將其解析為。

對於OP給出的代碼示例,修復將是這樣的:

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

請注意,最后一位是未經測試的代碼。 但是無論如何你應該參考Daniels的答案,因為我覺得他對'更好的選擇'是正確的。 但如果你現在沒有時間(比如我),你可以使用我的解決方案選項。 只需添加一個TODO,這樣您就可以利用您所承受的技術債務:)。

當我這樣做時,我會看到我是否可以使用Autofac的工作代碼來更新這個答案,該代碼遵循他的“更好的選擇”。 首先,我想仔細閱讀這篇文章 在快速閱讀中,在我看來,Autofac人員正在推廣使用“服務定位器”來處理生命周期范圍。 但根據馬克·西曼的說法,這是一種反模式 ,所以我有一些東西要弄明白......任何有專家意見的DI專家?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM