简体   繁体   中英

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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