Comment devrais-je injecter (en utilisant l'injection de dépendance standard) une instance de DbContext
dans un IHostedService
?
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).
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>();
…
}
}
…
}