Как мне добавить экземпляр DbContext в службу IHostedService?
Вопрос
Как я должен вводить (используя стандартную инъекцию зависимостей) экземпляр DbContext
в IHostedService
?
Что я пробовал
В настоящее время у меня класс IHostedService
принимает экземпляр MainContext
(вывод из DbContext
) в конструкторе.
Когда я запускаю приложение, я получаю:
Нельзя использовать ограниченную службу "Microsoft.EntityFrameworkCore.DbContextOptions" из singleton "Microsoft.Extensions.Hosting.IHostedService".
Итак, я попытался сделать переходный период DbContextOptions
, указав:
services.AddDbContext<MainContext>(options =>
options.UseSqlite("Data Source=development.db"), ServiceLifetime.Transient);
в моем классе Startup
.
Но ошибка остается прежней, хотя, согласно эта решена проблема Github, переданный DbContextOptions
должен иметь такое же время жизни, указанное в вызов AddDbContext
.
Я не могу сделать контекст базы данных singleton, в противном случае одновременные вызовы к нему приведут к исключениям concurrency (из-за того, что контекст базы данных не гарантируется потоком).
Ответы
Ответ 1
Хорошим способом использования сервисов внутри размещенных сервисов является создание области, когда это необходимо. Это позволяет использовать контексты службы /db и т.д. С конфигурацией жизненного цикла, с которой они настроены. Не создавая области, теоретически может привести к созданию одноэлементных DbContexts и неправильному использованию контекста (EF Core 2.0 с пулами DbContext).
Чтобы сделать это, введите IServiceScopeFactory
и используйте его для создания области, когда это необходимо. Затем разрешите любые зависимости, которые вам нужны из этой области. Это также позволяет регистрировать настраиваемые сервисы в виде зависимых от области действия, если вы хотите переместить логику из размещенной службы и использовать размещенную службу только для запуска некоторой работы (например, для запуска задачи регулярно, это будет регулярно создавать области, создавать службу задач в эта область, которая также получает контекст db, введенный).
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>();
…
}
}
…
}
Ответ 2
Спасибо Мартину за ответ - просто небольшое изменение в приведенном выше коде, если появляется эта ошибка:
InvalidOperationException: ни один сервис для типа 'x' не был зарегистрирован
Меняться от:
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
Для того, чтобы:
var dbContext = (MyDbContext)scope.ServiceProvider.GetRequiredService<IMyDbContext>();
Ответ 3
У меня есть подробный пример здесь, включая шаблон стратегии
https://github.com/danielhunex/HostedServices-dotnetcore