我具有以下界面
internal interface IScopedProcessingService
{
Task DoWork(CancellationToken stoppingToken);
}
和实施
public class ConsumeScopedServiceHostedService : BackgroundService
{
private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service running.");
await DoWork(stoppingToken);
}
private async Task DoWork(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is stopping.");
await Task.CompletedTask;
}
}
此代码来自官方的Microsoft文档。 Background scoped services
就像在文档中一样,我有ScopedProcessingService
,但要困难一些。这是代码:
internal class ScopedProcessingService : IScopedProcessingService
{
private int _executionCount;
private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
private readonly IPushRepository _pushRepository;
private readonly IPushTemplateRepository _pushTemplateRepository;
private readonly ISenderLogRepository _senderLogRepository;
private readonly IDistributionRepository _distributionRepository;
// services
private readonly IPushTemplateService _pushTemplateService;
private readonly ISendPushService _sendPushService;
public ScopedProcessingService(
ILogger<ConsumeScopedServiceHostedService> logger,
IPushTemplateService pushTemplateService, ISendPushService sendPushService,
IPushRepository pushRepository,
ISenderLogRepository senderLogRepository, IDistributionRepository distributionRepository,
IPushTemplateRepository pushTemplateRepository)
{
_logger = logger;
_pushTemplateService = pushTemplateService;
_sendPushService = sendPushService;
_pushRepository = pushRepository;
_senderLogRepository = senderLogRepository;
_distributionRepository = distributionRepository;
_pushTemplateRepository = pushTemplateRepository;
}
public async Task DoWork(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_executionCount = _senderLogRepository.SenderLogs.Count();
var logMessage = new StringBuilder();
logMessage.AppendLine($"Начинаю рассылку № {_executionCount}.");
// get all templates. THIS CALL IS A SOURCE OF PROBLEMS
var templates = _pushTemplateRepository.PushTemplates.Where(x => x.isActive)
.Include(x => x.Messages)
.ThenInclude(x => x.PushLang)
.Include(x => x.Category)
.Include(x => x.AdvertiserPushTemplates)
.ThenInclude(x => x.Advertiser)
.ToList();
}
}
在Startup.cs
类中,我使用以下代码来注入它:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
此行var templates = _pushTemplateRepository.PushTemplates.Where(x => x.isActive)
的问题。如果我使用PushTemplate
进行了某些更改,则此更改将不会在后台任务中生效。我将处理旧数据。我的意思是,如果我改名字
例如,对于PushTemplate
,从Name_1
到Name_2
的id = 15,我将在后台任务中拥有Name_1
。
如何正确将EF
注入到作用域范围内的后台服务中?我没有使用清晰的EF上下文。我有存储库层。
public interface IPushTemplateRepository
{
IQueryable<PushTemplate> PushTemplates { get; }
void Save(PushTemplate pushTemplate);
void Delete(int templateid);
}
实施
public class PushTemplateRepository : IPushTemplateRepository
{
private readonly ApplicationDbContext _applicationContext;
public PushTemplateRepository(ApplicationDbContext applicationContext)
{
_applicationContext = applicationContext;
}
public IQueryable<PushTemplate> PushTemplates => _applicationContext.PushTemplates;
public void Save(PushTemplate pushTemplate)
{
// ...
}
public void Delete(int templateid)
{
// ...
}
}
答案 0 :(得分:3)
问题是在具有无限循环的单个作用域中捕获的DbContext
。
范围从未被处置过,因此将保留创建范围时拥有的数据。
每次需要所需功能时,都进行重构以将循环移出一个级别并创建一个新的作用域。
ConsumeScopedServiceHostedService
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
_logger.LogInformation("Consume Scoped Service Hosted Service is working.");
while (!stoppingToken.IsCancellationRequested) {
using (var scope = Services.CreateScope()) {
IServiceProvider serviceProvider = scope.ServiceProvider;
var service = serviceProvider.GetRequiredService<IScopedProcessingService>();
await service.DoWork(stoppingToken);
}
//Add a delay between executions.
await Task.Delay(SomeIntervalBetweenCalls, stoppingToken);
}
}
ScopedProcessingService
//...
public async Task DoWork(CancellationToken stoppingToken) {
_executionCount = _senderLogRepository.SenderLogs.Count();
var logMessage = new StringBuilder();
logMessage.AppendLine($"Начинаю рассылку № {_executionCount}.");
// get all templates.
var templates = _pushTemplateRepository.PushTemplates.Where(x => x.isActive)
.Include(x => x.Messages)
.ThenInclude(x => x.PushLang)
.Include(x => x.Category)
.Include(x => x.AdvertiserPushTemplates)
.ThenInclude(x => x.Advertiser)
.ToList();
//...
}