3

我有以下界面

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;
    }
}

此代码来自微软官方文档。后台范围服务

就像在文档中一样,我有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)。如果我使用 a 进行一些更改,PushTemplate此更改将不会对后台任务产生影响。我将处理旧数据。我的意思是,如果我更改 a 的名称PushTemplate,例如,将 id = 15 从Name_1to更改Name_2为我将Name_1在后台任务中使用的名称。

如何EF正确注入 Scoped 后台服务?我不使用明确的 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)
    {
      // ... 
    }
}
4

1 回答 1

8

DbContext问题是在具有无限循环的单个范围内捕获。

范围永远不会被释放,因此将保留它在创建范围时拥有的数据。

每次需要所需功能时,重构以将循环移出一个级别并创建一个新范围。

消费ScopedServiceHostedService

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();

    //...
}
于 2019-10-17T18:42:15.423 回答