繁体   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