Платформа сущностей с размещенными сервисами Scoped - PullRequest
2 голосов
/ 17 октября 2019

У меня есть следующий интерфейс

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. Фоновые сервисы с областями действия

И, как в документации, у меня есть 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, например, с id = 15 с Name_1 на 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)
    {
      // ... 
    }
}

1 Ответ

3 голосов
/ 17 октября 2019

Проблема заключается в том, что 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();

    //...
}
...