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

Miguel Moura picture Miguel Moura · Aug 1, 2016 · Viewed 66.6k times · Source

On an ASP.NET Core project I have the following on Startup:

  services.AddDbContext<Context>(x => x.UseSqlServer(connectionString));

  services.AddTransient<IValidationService, ValidationService>();

  services.AddTransient<IValidator<Model>, ModelValidator>();

The ValidationService is as follows:

public interface IValidationService {
    Task<List<Error>> ValidateAsync<T>(T model);
}

public class ValidationService : IValidationService {
    private readonly IServiceProvider _provider;

    public ValidationService(IServiceProvider provider) {
        _provider = provider;
    }

    public async Task<List<Error>> ValidateAsync<T>(T model) {
        IValidator<T> validator = _provider.GetRequiredService<IValidator<T>>();

        return await validator.ValidateAsync(model);
    }
}

And the ModelValidator is as follows:

public class ModelValidator : AbstractValidator<Model> {
  public ModelValidator(Context context) {
    // Some code using context
  }
}

When I inject a IValidationService in a controller and use it as:

List<Error> errors = await _validator.ValidateAsync(order);    

I get the error:

System.ObjectDisposedException: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur is you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. Object name: 'Context'.

Any idea why I am having this error when using Context inside ModelValidator.

How to fix this?

UPDATE

So I changed the code to:

services.AddScoped<IValidationService, ValidationService>();

services.AddScoped<IValidator<Model>, ModelValidator>();

But I get the same error ...

UPDATE - Seed Data Code inside Configure method on Startup

So on Configure method I have:

if (hostingEnvironment.IsDevelopment())
  applicationBuilder.SeedData();

And the SeedData extension is:

public static class DataSeedExtensions {
    private static IServiceProvider _provider;

    public static void SeedData(this IApplicationBuilder builder) { 
        _provider = builder.ApplicationServices;
        _type = type;

        using (Context context = (Context)_provider.GetService<Context>()) {
            await context.Database.MigrateAsync();
            // Insert data code
    }
}

What am I missing?

UPDATE - A possible solution

Changing my Seed method to the following seems to work:

using (IServiceScope scope = 
    _provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
    Context context = _provider.GetService<Context>();
    // Insert data in database
}

Answer

Peter picture Peter · May 9, 2017

Just a guess in what causes your error:

You are using DI and async calls. If somewhere in your call stack you return a void instead of Task you get the described behavior. At that point the call is ended and the context disposed. So check if you have a async call that returns a void instead of Task. If you change the return value, the objectdisposedexception is probably fixed.

public static class DataSeedExtensions {
private static IServiceProvider _provider;

public static async Task SeedData(this IApplicationBuilder builder) { //This line of code

  _provider = builder.ApplicationServices;
  _type = type;

  using (Context context = (Context)_provider.GetService<Context>()) {

    await context.Database.MigrateAsync();
    // Insert data code

  }

}

And in configure:

if (hostingEnvironment.IsDevelopment()){
   await  applicationBuilder.SeedData();
}

Blog post on how to fix this error: cannot-access-a-disposed-object-in-asp-net-core-when-injecting-dbcontext