Wie soll ich eine DbContext-Instanz in einen IHostedService einfügen?


74

Frage

Wie soll ich (unter Verwendung der Standard-Abhängigkeitsinjektion) eine DbContextInstanz in eine Instanz injizieren IHostedService?

Was habe ich versucht?

Ich lasse meine IHostedServiceKlasse derzeit eine MainContext(abgeleitete DbContext) Instanz im Konstruktor übernehmen.

Wenn ich die Anwendung starte, bekomme ich:

Der Scoped-Service 'Microsoft.EntityFrameworkCore.DbContextOptions' kann nicht vom Singleton 'Microsoft.Extensions.Hosting.IHostedService' verwendet werden.

Also habe ich versucht, den DbContextOptionsÜbergang durch Angabe von:

services.AddDbContext<MainContext>(options => 
                options.UseSqlite("Data Source=development.db"), ServiceLifetime.Transient);

in meiner StartupKlasse.

Der Fehler bleibt jedoch derselbe, obwohl gemäß diesem gelösten Github-Problem die DbContextOptionsübergebene Lebensdauer dieselbe im AddDbContextAufruf angegebene Lebensdauer haben sollte .

Ich kann den Datenbankkontext nicht zu einem Singleton machen, da sonst gleichzeitige Aufrufe zu Parallelitätsausnahmen führen würden (da nicht garantiert wird, dass der Datenbankkontext threadsicher ist).


Vielleicht stattdessen eine Kontextfabrik injizieren? zB stackoverflow.com/a/11748370/1663001
DavidG

Antworten:


140

Eine gute Möglichkeit, Dienste innerhalb gehosteter Dienste zu verwenden, besteht darin, bei Bedarf einen Bereich zu erstellen. Dies ermöglicht die Verwendung von Diensten / Datenbankkontexten usw. mit der Lebensdauerkonfiguration, mit der sie eingerichtet wurden. Wenn kein Bereich erstellt wird, kann dies theoretisch dazu führen, dass einzelne DbContexts erstellt und der Kontext nicht ordnungsgemäß wiederverwendet wird (EF Core 2.0 mit DbContext-Pools).

Fügen IServiceScopeFactorySie dazu ein ein und erstellen Sie bei Bedarf einen Bereich. Lösen Sie dann alle Abhängigkeiten, die Sie von diesem Bereich benötigen. Auf diese Weise können Sie auch benutzerdefinierte Dienste als Abhängigkeiten mit Gültigkeitsbereich registrieren, wenn Sie die Logik aus dem gehosteten Dienst verschieben und den gehosteten Dienst nur zum Auslösen von Arbeiten verwenden möchten (z. B. regelmäßig eine Aufgabe auslösen - dies würde regelmäßig Bereiche erstellen und den Aufgabendienst in erstellen dieser Bereich, in den auch ein Datenbankkontext eingefügt wird).

public class MyHostedService : IHostedService
{
    private readonly IServiceScopeFactory scopeFactory;

    public MyHostedService(IServiceScopeFactory scopeFactory)
    {
        this.scopeFactory = scopeFactory;
    }

    public void DoWork()
    {
        using (var scope = scopeFactory.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
            …
        }
    }
    …
}

3
Vielen Dank. Was ist der Unterschied zwischen Injizieren IServiceScopeFactoryund IServiceProviderdirektem Injizieren ?
Schuh

3
IServiceProvidergibt Ihnen nur den Root-Dienstanbieter. Während es auch die Schnittstelle implementiert, um einen Bereich zu erstellen, können Sie ihn verwenden. Die allgemeine Regel ist jedoch, so wenig wie nötig anzufordern.
Martin Ullrich

Wenn ich DoWorkinnerhalb der usingKlausel einen neuen Thread (der die Lebensdauer überlebt ) spawne und einige der Dienste verwende, die ich darin abgerufen habe , ist das in Ordnung ? Oder definiert der Geltungsbereich, wann ein Dienst veräußert wird?
Schuh

Der Umfang definiert, wann Dinge entsorgt werden. Beachten Sie, dass Datenbankkontexte bei Verwendung von Pooling möglicherweise von einer anderen Komponente wiederverwendet werden. Ich habe nur Code für gehostete Dienste gehostet, der async / await verwendet. await Task.Delay(…)Ich möchte gerne lange laufende geplante Arbeiten ausführen .
Martin Ullrich

7
Die AddDbContext()von EF bereitgestellten Standardmethoden registrieren es nur als Gültigkeitsbereich. Am Ende des Bereichs führt EF beispielsweise eine Bereinigung durch. Sie möchten keinen Singleton-Datenbankkontext in Webanwendungen haben, da sonst alle Ihre Komponenten die Transaktionen anderer Komponenten beeinträchtigen würden. Alle Dienste, die DB-Kontextinstanzen verwenden (über Konstruktorinjektion), müssen ebenfalls einen Gültigkeitsbereich haben.
Martin Ullrich
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.