AspNet Core Autofac disposing my DbContext even if its registered as SingleInstance

痞子三分冷 提交于 2021-01-28 21:18:55

问题


In our application, we have an api which will store some data to a database. We are using Entity Framework Core 3.1.1. After this entity has been stored, a message is posted to Azure Servicebus and a consumer will read this message a store the message to another table in the same DbContext.

As far as I understand, a LifetimeScope is defined per request to the api. The LifetimeScope will start when the api request hits the ApiController and finishes when the endpoint is done processing the request.

The issue we are facing is related to the DbContext being disposed, so it cannot be used in the Consumer sinced its disposed.

From our code its pretty obvious why it fails. The DbContext is registered this way:

containerBuilder.RegisterType<TDbContext>().AsSelf().InstancePerLifetimeScope().As<IDbContext>().IfNotRegistered(typeof(TDbContext));

We set the InstancePerLifetimeScope when registering the DbContext, resulting the DbContext to be disposed when the api request is finished and throwing ObjectDisposedException when we try to use it in the consumer.

Just as an experiment I tried registering the DbContext as SingleInstance this way:

containerBuilder.RegisterType<TDbContext>().AsSelf().SingleInstance().As<IDbContext>().IfNotRegistered(typeof(TDbContext));

But it still throws the ObjectDisposedException when we try using it in the Consumer.

Any ideas why this is happening and how we can solve this?

EDIT:

The DbContext is registered via an extension method and passed in as a generic:

public static class ContainerBuilderExtensions 
{
    public static void RegisterDbContext<TDbContext>(this ContainerBuilder builder) 
    {
        containerBuilder.RegisterType<TDbContext>().AsSelf().InstancePerLifetimeScope().As<IDbContext>().IfNotRegistered(typeof(TDbContext));
    }
}

回答1:


Assuming that you are calling this

containerBuilder.RegisterType<TDbContext>().AsSelf().SingleInstance().As<IDbContext>().IfNotRegistered(typeof(TDbContext));

after this

containerBuilder.RegisterType<TDbContext>().AsSelf().InstancePerLifetimeScope().As<IDbContext>().IfNotRegistered(typeof(TDbContext));

the second registration will not have any effect.

Secondly, registering the DbContext as a singleton is not a very good idea. It's not thread-safe.

Instead, you can inject IServiceProvider in the class where the consumer for the servicebus is registered and resolve the context manually when a new message is being consumed.

Option 1: Using IServiceProvider

using (var scope = _serviceProvider.CreateScope())
{
   using (var context = scope.ServiceProvider.GetRequiredService<IDbContext>())
   {
      // do work here
   }
}

Option 2: Using IServiceProvider and casting down to ILifetimeScope using the extension available since Autofac.Extensions.Microsoft.DependencyInjection version 5.

using (var lifetimeScope = _serviceProvider.GetAutofacRoot().BeginLifetimeScope())
{
   using (var context = lifetimeScope.Resolve<IDbContext>())
   {
      // do work here
   }
}

This will create an instance of IDbContext everytime you need it and disposes it. While this might take a little longer, it shouldn't be a problem, when messages are consumed in the background.

EDIT: You can also register the DbContext as Transient

containerBuilder.RegisterType<TDbContext>().AsSelf().InstancePerDependency().As<IDbContext>().IfNotRegistered(typeof(TDbContext));


来源:https://stackoverflow.com/questions/60601854/aspnet-core-autofac-disposing-my-dbcontext-even-if-its-registered-as-singleinsta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!