2016-03-31 15 views
8

Ho bisogno di accedere al mio database in una classe Singleton istanziata nella mia classe Startup. Sembra che iniettare direttamente risultati in un DbContext che è disposto.Utilizzare DbContext in ASP .Net Singleton Injected Class

ottengo il seguente errore:

Cannot access a disposed object. Object name: 'MyDbContext'.

La mia domanda è duplice: Perché non questo lavoro e come posso accedere al mio database in un'istanza di classe Singleton?

Ecco il mio metodo ConfigureServices nella mia classe di avvio:

public void ConfigureServices(IServiceCollection services) 
{ 
    // code removed for brevity 

    services.AddEntityFramework().AddSqlServer().AddDbContext<MyDbContext>(
     options => 
     { 
      var config = Configuration["Data:DefaultConnection:ConnectionString"]; 
      options.UseSqlServer(config); 
     }); 

    // code removed for brevity 

    services.AddSingleton<FunClass>(); 
} 

Qui è la mia classe controller:

public class TestController : Controller 
{ 
    private FunClass _fun; 

    public TestController(FunClass fun) 
    { 
     _fun = fun; 
    } 

    public List<string> Index() 
    { 
     return _fun.GetUsers(); 
    } 
} 

Ecco il mio FunClass:

public class FunClass 
{ 
    private MyDbContext db; 

    public FunClass(MyDbContext ctx) { 
     db = ctx; 
    } 

    public List<string> GetUsers() 
    { 
     var lst = db.Users.Select(c=>c.UserName).ToList(); 
     return lst; 
    } 
} 
+1

Vedere [questa risposta] (http://stackoverflow.com/questions/36246896/structuremap-creation-as-transient-per-request-not-working/36249145#36249145). Un oggetto non può avere dipendenze con una vita più breve di se stesso. È possibile iniettare una fabbrica per creare istanze più brevi o refactoring in modo che la radice del grafico dell'oggetto non sia un singleton. – NightOwl888

+2

Sconsiglio vivamente di registrare il tuo 'DbContext' come Singleton, ci sono molti articoli sul web che ti dicono perché è una cattiva idea. Ecco una [risposta] (http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why) fornita dal creatore di [Simple Injector] (https://simpleinjector.org/index) .html) che cerca di spiegare perché. Suggerisco vivamente di utilizzare un modello come i modelli * Repository * o * Unit of Work *. – QuantumHive

+0

@QuantumHive grazie. Ho notato un avvertimento nella mia risposta di lavoro. – Tjaart

risposta

8

Il motivo doesn Il lavoro è dovuto al fatto che l'estensione .AddDbContext lo aggiunge come ambito per richiesta. Scoped per request è generalmente ciò che si desidera e in genere le modifiche di salvataggio verranno chiamate una volta per richiesta e quindi lo dbcontext verrà eliminato alla fine della richiesta.

Se davvero bisogno di usare un dbContext all'interno di un singleton, quindi la classe FunClass dovrebbe probabilmente prendere una dipendenza da IServiceProvider e DbContextOptions invece di prendere direttamente una dipendenza dal DbContext, in questo modo è possibile creare da soli.

public class FunClass 
{ 
    private GMBaseContext db; 

    public FunClass(IServiceProvider services, DbContextOptions dbOptions) 
    { 
     db = new GMBaseContext(services, dbOptions); 
    } 

    public List<string> GetUsers() 
    { 
     var lst = db.Users.Select(c=>c.UserName).ToList(); 
     return lst; 
    } 
} 

Detto questo, il mio consiglio è di valutare attentamente se si ha realmente bisogno i vostri FunClass essere un Singleton, vorrei evitare che se non si dispone di una buona ragione per fare un Singleton.

+1

Il passaggio del contenitore o 'IServiceProvider' è un anti-pattern in quanto associa i tuoi tipi a un contenitore specifico (' IServiceProvider' in questo caso) e dovrebbe essere evitato.si dovrebbe usare sia il metodo factory o factory class/interface per implementarlo. il metodo factory può essere implementato come 'services.AddSingleton (services => new FunClass (new GMBaseContext));'. Se sono necessari servizi applicativi aggiuntivi, è possibile risolverli con il metodo factory tramite 'services.RequestService ()' e passarlo al costruttore di 'GMBaseContext'. – Tseng

+0

sì, sono d'accordo sarebbe meglio evitarlo, ma penso che troverete i luoghi in cui è usato nel quadro stesso. cioè il costruttore di DBContext, ma ora che ho guardato il codice più recente, sembra che IServiceProvider non sia più necessario nel costruttore per DBContext nell'ultimo codice in modo da poterlo evitare, ma penso che come da RC1 ne abbia bisogno in il costruttore per ottenere un DBContext che funzioni –

+0

Non riesco a creare un'istanza del mio DbContext con DbContextOptions perché il mio contesto deriva da IdentityDbContext , quindi non esiste un costruttore di base per passare a DbContextOptions. – Tjaart

2

La soluzione era quella di chiamare AddSingleton con la mia classe di essere istanziata nel parametro del metodo nella mia classe di avvio:

services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"]))); 

La soluzione era quello di cambiare la mia classe DbContext:

public class MyContext : IdentityDbContext<ApplicationUser> 
{ 
    private string connectionString; 

    public MyContext() 
    { 

    } 

    public MyContext(DbContextOptions options, string connectionString) 
    { 
     this.connectionString = connectionString; 
    } 

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
    { 
     // Used when instantiating db context outside IoC 
     if (connectionString != null) 
     { 
      var config = connectionString; 
      optionsBuilder.UseSqlServer(config); 
     } 

     base.OnConfiguring(optionsBuilder); 
    } 

} 

Come più persone ho comunque avvertito, l'utilizzo di un DbContext in una classe singleton potrebbe essere una pessima idea. Il mio uso è molto limitato nel codice reale (non nell'esempio di FunClass), ma penso che se lo farai sarebbe meglio trovare altri modi.

+0

Qualcuno ha letto questo articolo? http://mehdi.me/ambient-dbcontext-in-ef6/ Penso che sia promettente - per me ha più senso per le app Web utilizzare una factory piuttosto che un dbcontext puramente iniettato - anche se forse più per le applicazioni web con un sacco di DI config dove forse un guadagno di prestazioni sta usando singleton in contrapposizione con le durate delle richieste web. – Andez

+0

Si noti che il costruttore 'public MyContext (DbContextOptions options, string connectionString) 'ignora il parametro options. Va bene? –