简体   繁体   English

DbContext已经处理掉并且是autofac

[英]DbContext has been disposed and autofac

I have a controller: 我有一个控制器:

private readonly ILogger _logger;    
private readonly IRepository _repository;

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

This is the 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;
         }
    }
}

Finally, registration types in IoC: 最后,IoC中的注册类型:

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

When I run the application I get this error: 当我运行应用程序时,我收到此错误:

The operation cannot be completed because the DbContext has been disposed. 由于已经处理了DbContext,因此无法完成操作。

I tried to change registration EfRepository like this: 我试着改变注册EfRepository,如下所示:

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

In this case the first request finish, but when trying to open other pages, I get the error again. 在这种情况下,第一个请求完成,但在尝试打开其他页面时,我再次收到错误。 Where is the problem? 问题出在哪儿?

When using the WithParameter method, the parameter instance will be the same for every resolved object. 使用WithParameter方法时,参数实例对于每个已解析的对象都是相同的。 So with .WithParameter("context", new PcpContext()) you are effectively using the same instance of the PcpContext class for any resolved instance of IRepository. 因此,使用.WithParameter("context", new PcpContext())您可以有效地为任何已解析的IRepository实例使用PcpContext类的相同实例。

With your current code, when an IRepository instance is disposed, it will also dispose that PcpContext instance. 使用当前代码,当处理IRepository实例时,它还将处置该PcpContext实例。 Then any subsequent attempt to resolve an IRepository will receive the PcpContext instance that was disposed. 然后,任何后续尝试解析IRepository都将接收已处置的PcpContext实例。 You need a way to receive a fresh new instance of the EF DbContext on each Http Request that is disposed of at the end of the request. 您需要一种方法在每个Http请求上接收一个新的EF DbContext实例,该请求在请求结束时处理。

One option could be to register a code block for the IRepository so that code block is executed every time an IRepository needs to be resolved: 一种选择可以是为IRepository注册代码块,以便每次需要解析IRepository时执行代码块:

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

A better option would be to create a new IDatabaseContext abstraction, updating EfRepository so it depends on the new IDatabaseContext abstraction instead of the PcpContext class (Which may already be the case :) ). 更好的选择是创建一个新的IDatabaseContext抽象,更新EfRepository所以这取决于新IDatabaseContext抽象,而不是PcpContext类(可能已经是这样的:))。

The implementation class for IDatabaseContext will be your PcpContext class, which must inherit from the EF DbContext and probably receive the connection string as a parameter. 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
}

This gets better with the idea of using an IoC container and leaving the burden of lifetime management to them. 使用IoC容器并将生命周期管理负担留给他们的想法变得更好。 Now your Repository class does not need to be disposable nor needs to manage and dispose of its IDatabaseContext dependency. 现在,您的Repository类不需要是一次性的,也不需要管理和处理其IDatabaseContext依赖项。 It is Autofac who will keep track of the context instance and dispose of it when appropriate. Autofac将跟踪上下文实例并在适当时处理它。

For the same reason, you probably want to use InstancePerLifetimeScope with the database context dependency. 出于同样的原因,您可能希望将InstancePerLifetimeScope与数据库上下文相关性一起使用。 That would mean the same EF context is shared for every repository instance on the same Http request and is disposed at the end of the request. 这意味着在同一个Http请求上为每个存储库实例共享相同的EF上下文,并在请求结束时进行处理。

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

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

I went with the simple solution of a 'code block' as @Daniel JG suggested (a lambda). 我使用@Daniel JG建议的“代码块”的简单解决方案(lambda)。

Below a code example of this in Autofac. 下面是Autofac中的代码示例。 Daniels' example is for Unity as he also mentions himself. 丹尼尔斯的例子是Unity,因为他也提到了自己。 Because the OP added Autofac as a tag this seemed relevant to me: 因为OP将Autofac添加为标签,这似乎与我相关:

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

That code solved the DbContext has been disposed issue I was having with Entity Framework. 该代码解决了DbContext has been disposed我与Entity Framework有关的问题。 Note that compared to most other DI containers - including Unity - Autofac switches around the thing registered and the the thing it resolves to. 请注意,与大多数其他DI容器(包括Unity)相比,Autofac会在已注册的东西周围进行切换,并将其解析为。

For the code example given by the OP the fix would be something like this: 对于OP给出的代码示例,修复将是这样的:

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

Note that this last bit is untested code. 请注意,最后一位是未经测试的代码。 But you should refer to Daniels answer for more info anyway, because I think he is right with the 'better option'. 但是无论如何你应该参考Daniels的答案,因为我觉得他对'更好的选择'是正确的。 But you might use my solution option if you don't have time right now (like me). 但如果你现在没有时间(比如我),你可以使用我的解决方案选项。 Just add a TODO so you can make good on the technical debt you're incurring :). 只需添加一个TODO,这样您就可以利用您所承受的技术债务:)。

When I do I'll see if I can update this answer with working code for Autofac that follows his 'better option'. 当我这样做时,我会看到我是否可以使用Autofac的工作代码来更新这个答案,该代码遵循他的“更好的选择”。 First I want to carefully read this article . 首先,我想仔细阅读这篇文章 On quick reading it seems to me the Autofac people are promoting using the 'Service Locator' for handling lifetime scope. 在快速阅读中,在我看来,Autofac人员正在推广使用“服务定位器”来处理生命周期范围。 But according to Mark Seemann that is an anti-pattern , so I have some stuff to figure out... Any DI expert with an opinion? 但根据马克·西曼的说法,这是一种反模式 ,所以我有一些东西要弄明白......任何有专家意见的DI专家?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM