Cannot Access a Disposed Object in ASP.NET Core When Injecting Dbcontext

Cannot access a disposed object. A common cause of this error is disposing a context

This is because of your method return type async void. In general, when you are using async void in your code it’s bad news, because:

  • You can’t wait for its completion
  • Any unhandled exceptions will terminate your process (ouch!)

So return async Task instead of async void from your method as follows:

public async Task OnGet(int id)
{
Book = await _db.Books.SingleOrDefaultAsync(x => x.Id == id);

if(Book == null)
{
RedirectToPage("Index");
}
}

For more details:

  • C# – beware of async void in your code

  • Cannot access a disposed object in ASP.NET Core when injecting DbContext

Cannot access a disposed object in ASP.NET Core when injecting DbContext

Update for ASP.NET Core 2.1

In ASP.NET Core 2.1 the methods changed slightly. The general method is similar to the 2.0, just the methods name and return types have been changed.

public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.Seed();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return new WebHostBuilder()
...; // Do not call .Build() here
}

Applies for ASP.NET Core 2.0

With ASP.NET Core 2.0 there have been some changes in how EF Core tools (dotnet ef migrations etc.) determine the DbContext and connection string at design time.

The below answer leads that the migrations and seeding are applied when calling any of the dotnet ef xxx commands.

The new pattern for getting a design time instance for the EF Core tools is by using an BuildHostWeb static method.

As per this announcement, EF Core will now use the static BuildWebHost method which configures the whole application, but doesn't run it.

  public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);

host.Run();
}

// Tools will use this to get application services
public static IWebHost BuildWebHost(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
}

Replace this in your old Main method

public static void Main(string[] args)
{
var host = BuildWebHost(args)
.Seed();

host.Run();
}

Where Seed is an extension method:

public static IWebHost Seed(this IWebHost webhost)
{
using (var scope = webhost.Services.GetService<IServiceScopeFactory>().CreateScope())
{
// alternatively resolve UserManager instead and pass that if only think you want to seed are the users
using (var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
SeedData.SeedAsync(dbContext).GetAwaiter().GetResult();
}
}
}

public static class SeedData
{
public static async Task SeedAsync(ApplicationDbContext dbContext)
{
dbContext.Users.Add(new User { Id = 1, Username = "admin", PasswordHash = ... });
}
}

Old Answer, still applies to ASP.NET Core 1.x

There is a semi-official pattern on how to seed Entity Framework Core in ASP.NET Core application you should apply, because during application startup there is no Request and hence no RequestServices (which resolves scoped services).

In essence it boils down to creating a new scope, resolve the types you need and dispose the scope again once you're finished.

// serviceProvider is app.ApplicationServices from Configure(IApplicationBuilder app) method
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<AppDbContext>();

if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}

One of the reasons directly resolving a service via app.ApplicationServices.GetService<MyService>() is that ApplicationServices is the application (or lifetime) scope provider and the services resolved here stay alive until the application is shut down.

Usually the scoped container will resolve from it's parent container, if the object already exists there. So if you instantiate the DbContext this way in the application, it will be available in ApplicationServices container and when a request happens, a child container will be created.

Now when resolving the DbContext it won't be resolved as scoped, because it already exists in the parent container, so the instance of the parent container will be returned instead. But since it has been disposed during the seeding, it won't be accessible.

A scope container is nothing else then a singleton container with limited lifetime.

So never resolve scoped services in Application startup w/o using the pattern above of first creating a scope and resolving from it.

C#: Cannot access a disposed object in ASP.NET Core when injecting DbContext with IQueryable

Scope automatically disposes scoped(and transient) objects implementing IDisosable which it created, so when the GetAddressTypes ends, scope and db will be disposed. So you either need to return some wrapper which client will dispose, or materialize query and return result from the method:

[EnableQuery]
public Task<List<LkAddressType>> GetAddressTypes()
{
using (var scope = _factory.CreateScope())
{
var db = scope.ServiceProvider.GetService<PropertyContext>();
return await db.LkAddressType.AsNoTracking().ToListAsync();
}
}

Or inject your context directly into the repository and let framework figure out scopes:

public class AddressRepository
{
private readonly PropertyContext _ctx;
public AddressRepository(PropertyContext ctx,IServiceScopeFactory factory) : base(factory)
{
_ctx= ctx;
}

[EnableQuery]
public IQueryable<LkAddressType> GetAddressTypes()
{
return _ctx.LkAddressType.AsNoTracking();
}
}

Cannot access a disposed context instance EF core

In asp.net core the DbContext should be a Scoped service, not a Singleton.

Change this:

services.AddDbContext<XXX>
(option =>
option.UseSqlServer(Configuration.GetConnectionString("YYY"))
.ServiceLifetime.Singleton);

To

services.AddDbContext<XXX>
(option =>
option.UseSqlServer(Configuration.GetConnectionString("YYY")));

Cannot access a disposed context instance

Solved the issue! As DavidG pointed out in his comment, I missed the 'await' keyword at certain places like AdministrationApiController.
Posting as an 'Answer' here, as I am unable to mark the comment as 'Answer'.

Cannot access a disposed context when trying to access EF Core DbContext in Startup ExceptionHandler

This is more of a design issue as, at the time of resolving and injecting the desired dependencies, they will be using the startup's service provider and not the request's service provider.

I would first suggest resolving the service via the current request's context in the delegate

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
} else {
app.UseExceptionHandler(options => {
options.Run(
async context => {
var ex = context.Features.Get<IExceptionHandlerFeature>();
if (ex != null) {
IEmailService emailService = context.RequestServices.GetRequiredService<IEmailService>();
await emailService.SendErrorEmailAsync(context, ex);
context.Response.Redirect($"/Error/{context.Response.StatusCode}");
}
});
});
app.UseStatusCodePagesWithReExecute("/Error/{0}");
app.UseHsts();
}

//...
}

Also refactor the service to avoid passing the service provider.

private readonly MyApplicationContext dbContext;
private readonly IUserService userService;

public EmailService(MyApplicationContext dbContext, IUserService userService) {
this.userService = userService;
this.dbContext = dbContext;
}

public async Task SendErrorEmailAsync(HttpContext context, Exception ex) {
var user = await userService.GetCurrentUserAsync();

//...build email, send, etc
}

Asp.Net Core Cannot access a disposed context instance

I'm pretty sure that TimerManager is your issue. You did not show its declaration but looks like its constructor accepts a callback to be called at some later point of time. And that's the issue. Your scoped service _service is captured in the callback and used at some later point of time when the request has already ended. So after the request ended, the DbContext is disposed and your _service will consume a disposed context.

The fix is to simply get the data first before passing it into your callback so that the _service will not be captured into that callback, like this:

public async Task<IActionResult> Get()
{
var liveMonitorings = await _service.GetAllAsync();
var timerManager = new TimerManager(async () => await _hub.Clients.All.SendAsync("transferchartdata", liveMonitorings));
return Ok(new { Message = "Request Completed" });
}

We need to change the returned type of Get to Task<IActionResult> to support async call.

If you actually want to call _service.GetAllAsync() at some time later (not at the time of requesting Get) inside the callback, you need to inject an IServiceScopeFactory to create a scope for your service inside that callback, like this:

public IActionResult Get([FromServices] IServiceScopeFactory serviceScopeFactory)
{
var timerManager = new TimerManager(async () =>
{
using(var scope = serviceScopeFactory.CreateScope()){
var service = scope.ServiceProvider.GetRequiredService<ILiveMonitoringService>(); ​
​var liveMonitorings = await service.GetAllAsync();
​return await _hub.Clients.All.SendAsync("transferchartdata", liveMonitorings);
​ }
​});
​return Ok(new { Message = "Request Completed" });
}

This way you don't need to inject your _service into the controller's constructor (because it's not used at all).

Cannot access a disposed object when injecting DbContext through Azure Function

It's hard to tell with the code you've provided, but the most likely culprit is that you aren't materializing the result set until the context has gone out of scope. Again, we can't see all the code, but this happens when you do things like return an IQueryable directly. Any type of service call should return a materialized list (i.e. call ToList() or ToListAsync() on the result set before returning). It might also be caused by lazy-loading, if you've enabled that feature. If so, yo should make sure that all necessary relationships are eagerly loaded.

It's also weird that you're newing up your service with an injected context. You should simply inject your service, and since it has a constructor dependency on your context, that will be automatically injected into it. That ensures that both objects are operating in the same, or at least compatible, lifetime.

Also, don't use IDisposable with injected dependencies. We can't tell anything about your service class, but if it does implement IDisposable, remove that.

DbContext Depedency Injection Issue - System.ObjectDisposedException: Cannot access a disposed object

And this would be why statics should be avoided. Virtually every time you have a static like this, some developer is going to trip over it, because they aren't considering how things actually work.

The static keyword isn't magic. You've got a scoped service where you want to persist state (your timer), so you just slap a static on it and call it a day. However, this service uses other scoped services (your context), which now are out of sync with this static timer, i.e. the timer sticks around, but the context doesn't.

First, if you need to maintain state across an application lifetime, you should be using a singleton scope. That frees you from the terror of static. However, then, you'll need to utilize the server-locator pattern to get your context, because you cannot inject a scoped instance into a singleton.

public class MyDbWatch : IMyDbWatch
{
private readonly IServiceProvider _serviceProvider;
private readonly Timer _timer;
private AutoResetEvent _autoEvent = null;

public MyDbWatch(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;

_autoEvent = new AutoResetEvent(false);
_timer = new Timer(
callback: async s => await OnTimerEventAsync(s),
state: _autoEvent,
dueTime: 5000,
period: 10000);
}

public async Task OnTimerEventAsync(Object stateInfo)
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();

Console.WriteLine("retreiving from db - 1");
var ienStates = from m in context.IenState select m;
Console.WriteLine("retreiving from db - 2");
var listdb = await ienStates.ToListAsync();
Console.WriteLine("retreiving from db - 3");
}
}
}

Then, in ConfigureServices:

services.AddSingleton<IMyDbWatch, MyDbWatch>(); 

Now, I have no idea what you're actually trying to accomplish with any of this, as your code doesn't make much sense, but the above is the only way you're going to safely be able to do it.



Related Topics



Leave a reply



Submit