因為 AddHostedService 和 DbContext 生命週期因素,一併把 .NET DI 知識補齊,參考文章如下
- Use scoped services within a BackgroundService
- .NET dependency injection
- Are You Using the DbContext in Background Services PROPERLY?
- 在 ASP.NET Core Singleton 生命週期服務引用 Scoped 物件
理解 DI 生命週期後就知道為什麼要透過 IServiceProvider 來存取 DbContext 囉
JobService
透過 IServiceProvider 來抓取 DbContext,而非直接把 DbContext DI 進 JokeService 內
Program.cs
執行結果
透過 IServiceProvider 來抓取 DbContext,而非直接把 DbContext DI 進 JokeService 內
using EFCoreWorkerSample.Models;
using Microsoft.EntityFrameworkCore;
namespace EFCoreWorkerSample
{
public class JokeService
{
private readonly IServiceProvider _serviceProvider;
public JokeService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public string GetJoke()
{
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EfcoreDbContext>();
// 從 DB 隨機抓取一個 Joke 出來顯示
string tsql = @"
SELECT TOP 1 *
FROM Jokes
ORDER BY NEWID()
";
var joke = dbContext.Jokes.FromSqlRaw(tsql).Single();
return $@"
輸出時間:{DateTime.Now}
問題:{joke.Q}
答案:{joke.A}";
}
}
}
改寫 Worker 內容
namespace EFCoreWorkerSample
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly JokeService _jokeService;
public Worker(ILogger<Worker> logger, JokeService jokeService)
{
_logger = logger;
_jokeService = jokeService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
string message = _jokeService.GetJoke();
_logger.LogInformation(message);
await Task.Delay(1000, stoppingToken);
}
}
}
}
Program.cs
Service 擴充方法預設生命週期
- AddHostedService:singletion
- AddDbContext:scoped
using EFCoreWorkerSample.Models;
namespace EFCoreWorkerSample
{
public class Program
{
public static void Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
// 使用 AddHostedService 來註冊實作 IHostedService 類別,其生命週期為 Singleton
builder.Services.AddHostedService<Worker>();
builder.Services.AddScoped<EfcoreDbContext>();
builder.Services.AddSingleton<JokeService>();
var host = builder.Build();
host.Run();
}
}
}
執行結果
DI 生命週期驗證
因為 AddHostedService 註冊 Worker 生命週期為 singletion,所以 JokeService DI 進去時也只能使用 singletion,換成其他生命週期 (scoped 或 transient),就會拋出 exception 啦
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: EFCoreWorkerSample.Worker': Cannot consume scoped service 'EFCoreWorkerSample.JokeService' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.)
- 參考資料
- SQL 查詢
沒有留言:
張貼留言