web-dev-qa-db-fra.com

Comment dois-je injecter une instance de DbContext dans un IHostedService?

Question

Comment devrais-je injecter (en utilisant l'injection de dépendance standard) une instance de DbContext dans un IHostedService?

Qu'ai-je essayé

Actuellement, ma classe IHostedService prend une instance MainContext (dérivant de DbContext) dans le constructeur.

Lorsque je lance l'application, je reçois:

Impossible d'utiliser le service défini 'Microsoft.EntityFrameworkCore.DbContextOptions' de singleton 'Microsoft.Extensions.Hosting.IHostedService'.

J'ai donc essayé de rendre le DbContextOptions transitoire en spécifiant:

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

dans ma Startup classe.

Mais l'erreur reste la même, même si, selon ce problème résolu avec Github , le DbContextOptions transmis devrait avoir la même durée de vie que celle spécifiée dans l'appel AddDbContext.

Je ne peux pas transformer le contexte de base de données en singleton, sans quoi des appels simultanés généreraient des exceptions de concurrence (en raison du fait qu'il n'est pas garanti que le contexte de la base de données est thread-safe).

28
Shoe

Un bon moyen d'utiliser des services à l'intérieur de services hébergés consiste à créer une étendue lorsque cela est nécessaire. Cela permet d'utiliser les contextes services/db, etc. avec la configuration de durée de vie avec laquelle ils sont configurés. Ne pas créer de périmètre pourrait en théorie conduire à la création de DbContexts uniques et à une réutilisation inappropriée du contexte (EF Core 2.0 avec les pools DbContext).

Pour ce faire, injectez un IServiceScopeFactory et utilisez-le pour créer une étendue si nécessaire. Ensuite, résolvez les dépendances dont vous avez besoin à partir de cette étendue. Cela vous permet également d'enregistrer des services personnalisés en tant que dépendances étendues si vous souhaitez déplacer la logique hors du service hébergé et utiliser le service hébergé uniquement pour déclencher un travail (par exemple, déclencher régulièrement une tâche - cela créerait régulièrement des étendues, créerait cette portée qui obtient également un contexte de base de données injecté).

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>();
            …
        }
    }
    …
}
63
Martin Ullrich