2016-03-31 13 views
8

Ich brauche Zugriff auf meine Datenbank in einer Singleton-Klasse in meiner Startup-Klasse instanziiert. Es scheint, dass das direkte Injizieren zu einem DbContext führt, der entsorgt wird.Verwenden Sie DbContext ASP.Net Singleton Injected Klasse

bekomme ich folgende Fehlermeldung:

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

Meine Frage ist zweierlei: Warum nicht diese Arbeit und wie kann ich meine Datenbank zugreifen in einer Singleton-Klasse-Instanz?

Hier meine ConfigureServices Methode in meine Startup-Klasse ist:

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

Hier meine Controller-Klasse ist:

public class TestController : Controller 
{ 
    private FunClass _fun; 

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

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

Hier ist meine 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

Siehe [diese Antwort] (http://stackoverflow.com/questions/36246896/structuremap-creation-as-transient-per-request-not-working/36249145#36249145). Ein Objekt kann keine Abhängigkeiten mit einer kürzeren Lebensdauer als sich selbst haben. Sie können entweder eine Factory injizieren, um Instanzen mit kürzerer Lebensdauer zu erstellen, oder eine Refactoring-Funktion ausführen, sodass der Stamm des Objektdiagramms kein Singleton ist. – NightOwl888

+2

Ich rate Ihnen dringend davon ab, Ihren 'DbContext' als Singleton zu registrieren, es gibt viele Artikel im Internet, die Ihnen sagen, warum es eine schlechte Idee ist. Hier ist eine [Antwort] (http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why) vom Ersteller von [Simple Injector] (https://simpleinjector.org/index) .html) das versucht zu erklären warum. Ich würde dringend empfehlen, ein Muster wie die Muster * Repository * oder * Unit of Work * zu verwenden. – QuantumHive

+0

@QuantumHive danke. Ich habe eine Warnung in meiner Arbeitsantwort notiert. – Tjaart

Antwort

8

Der Grund ist es doesn Es funktioniert nicht, weil die Erweiterung .AddDbContext es als Bereich für jede Anfrage hinzufügt. Scoped pro Anfrage ist in der Regel, was Sie wollen und in der Regel speichern Änderungen würden einmal pro Anfrage aufgerufen werden und dann würde die dbcontext am Ende der Anfrage angeordnet werden.

Wenn Sie wirklich eine dbContext in einem singleton verwenden müssen, dann FunClass Klasse sollte wahrscheinlich eine Abhängigkeit nehmen IServiceProvider und DbContextOptions statt direkt eine Abhängigkeit von der DbContext, so nehmen Sie es selbst erstellen können.

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

Das heißt, mein Rat sorgfältig wäre zu überlegen, ob Sie benötigen, um Ihre FunClass wirklich ein Singleton zu sein, würde ich das vermeiden, wenn Sie es zur Herstellung eines Singletons einen sehr guten Grund haben.

+1

Das Übergeben des Containers oder 'IServiceProvider' ist ein Antimuster, da es Ihre Typen an einen bestimmten Container bindet (in diesem Fall IServiceProvider) und daher vermieden werden sollte.Man sollte entweder Factory-Methode oder Factory-Klasse/Schnittstelle verwenden, um dies zu implementieren. Die Factory-Methode kann wie folgt implementiert werden: 'services.AddSingleton (services => new FunClass (new GMBaseContext));'. Wenn Sie zusätzliche Anwendungsdienste benötigen, können Sie sie innerhalb der Factory-Methode über 'services.RequestService ()' auflösen und an den Konstruktor von 'GMBaseContext' übergeben. – Tseng

+0

ja ich stimme zu, es wäre besser, es zu vermeiden, aber ich denke, Sie werden Orte finden, wo es im Rahmen selbst verwendet wird. Das ist der Konstruktor von DBContext, aber jetzt, da ich den neuesten Code angeschaut habe, scheint es, dass IServiceProvider im Konstruktor für DBContext im letzten Code nicht mehr benötigt wird, so dass man das vermeiden könnte, aber ich denke, dass er es von RC1 benötigt der Konstruktor, um einen DBContext zu erhalten, der funktioniert –

+0

Ich kann meinen DbContext mit DbContextOptions nicht instanziieren, weil mein Kontext von IdentityDbContext abgeleitet ist, also gibt es keinen Basiskonstruktor, um DbContextOptions zu übergeben. – Tjaart

2

Die Lösung war AddSingleton mit meiner Klasse zu nennen in dem Methodenparameter in meiner Startup-Klasse instanziiert wird:

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

Die Lösung meiner DbContext Klasse zu ändern war :

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

} 

Da mehrere Personen habe jedoch gewarnt, die Verwendung eines DbContext in einer Singleton-Klasse könnte eine sehr schlechte Idee sein. Mein Gebrauch ist im wirklichen Code sehr begrenzt (nicht das Beispiel FunClass), aber ich denke, wenn Sie dies tun, wäre es besser, andere Wege zu finden.

+0

Hat jemand diesen Artikel gelesen? http://mehdi.me/ambient-dbcontext-in-ef6/ Ich denke es ist vielversprechend - für mich macht es mehr Sinn für Web-Apps, eine Factory im Gegensatz zu einem rein injizierten dbcontext zu verwenden - obwohl möglicherweise mehr für Web-Anwendungen mit eine Menge von DI config, wo es vielleicht einen Leistungszuwachs gibt, verwendet Singleton im Gegensatz zu pro Webanforderungslebensdauern. – Andez

+0

Beachten Sie, dass der Konstruktor 'public MyContext (DbContextOptions-Optionen, String connectionString) 'den options-Parameter ignoriert. Ist es o.k? –