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


on asp.net core project have following on startup:

  services.adddbcontext<context>(x => x.usesqlserver(connectionstring));    services.addtransient<ivalidationservice, validationservice>();    services.addtransient<ivalidator<model>, modelvalidator>(); 

the validationservice 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 modelvalidator follows:

public class modelvalidator : abstractvalidator<model> {   public modelvalidator(context context) {     // code using context   } } 

when inject ivalidationservice in controller , use as:

list<error> errors = await _validator.validateasync(order);     

i error:

system.objectdisposedexception: cannot access disposed object. common cause of error disposing context resolved dependency injection , later trying use same context instance elsewhere in application.  may occur calling dispose() on context, or wrapping context in using statement.  if using dependency injection, should let dependency injection container take care of disposing context instances. object name: 'context'.     

any idea why having error when using context inside modelvalidator.

how fix this?

update

so changed code to:

services.addscoped<ivalidationservice, validationservice>();  services.addscoped<ivalidator<model>, modelvalidator>(); 

but same error ...

update - seed data code inside configure method on startup

so on configure method have:

if (hostingenvironment.isdevelopment())   applicationbuilder.seeddata(); 

and 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 missing?

update - possible solution

changing seed method following seems work:

using (iservicescope scope = _provider.getrequiredservice<iservicescopefactory>().createscope()) {    context context = _provider.getservice<context>();    // insert data in database  } 

new answer, applies asp.net core 2.x , newer

with asp.net core 2.0 there have been changes in how ef core tools (dotnet ef migartions etc.) determine dbcontext , connection string @ design time.

the below answer leads migrations , seeding applied when calling of dotnet ef xxx commands.

the new pattern getting design time instance ef core tools using buildhostweb static method.

as per this announcement, ef core use static buildwebhost method configures whole application, doesn't run it.

  public class program   {       public static void main(string[] args)       {           var host = buildwebhost(args);            host.run();       }        // tools use application services       public static iwebhost buildwebhost(string[] args) =>           new webhostbuilder()               .usekestrel()               .usecontentroot(directory.getcurrentdirectory())               .useiisintegration()               .usestartup<startup>()               .build();   } 

replace in old main method

public static void main(string[] args) {     var host = buildwebhost(args)         .seed();      host.run(); } 

where seed extension method:

public static iwebhost seed(this iwebhost webhost) {     using (var scope = webhost.services.getservice<iservicescopefactory>().createscope())     {         // alternatively resolve usermanager instead , pass if think want seed users         use(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 asp.net core 1.x

there semi-official pattern on how seed entity framework core in asp.net core application should apply, because during application startup there no request , hence no requestservices (which resolves scoped services).

in essence boils down creating new scope, resolve types need , dispose scope again once you're finished.

// serviceprovider app.applicationservices 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 reasons directly resolving service via app.applicationservices.getservice<myservice>() applicationservices application (or lifetime) scope provider , services resolved here stay alive until application shut down.

usually scoped container resolve it's parent container, if object exists there. if instantiate dbcontext way in application, available in applicationservices container , when request happens, child container created.

now when resolving dbcontext won't resolved scoped, because exists in parent container, instance of parent container returned instead. since has been disposed during seeding, won't accessible.

a scope container nothing else singleton container limited lifetime.

so never resolve scoped services in application startup w/o using pattern above of first creating scope , resolving it.


Comments