具有范围托管服务的实体框架

Ale*_*bak 3 c# entity-framework-core .net-core asp.net-core-hosted-services

我有以下界面

internal interface IScopedProcessingService
{
    Task DoWork(CancellationToken stoppingToken);
}
Run Code Online (Sandbox Code Playgroud)

和实施

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

This code is from official Microsoft documentation. Background scoped services

And like in the documentation I have ScopedProcessingService but a few more difficult. Here is the code:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

In the Startup.cs class I use the following code to inject it:

services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Run Code Online (Sandbox Code Playgroud)

The problem with this line var templates = _pushTemplateRepository.PushTemplates.Where(x => x.isActive). If I make some changes with a PushTemplate this changes will not have effect in the background task. And I will process old data. I mean, If I change name for a PushTemplate, for example, with id = 15 from Name_1 to Name_2 than I will have Name_1 in the background task.

How to inject EF in a Scoped background service correctly? I not use clear EF context. I have repository layer.

public interface IPushTemplateRepository
{
    IQueryable<PushTemplate> PushTemplates { get; }

    void Save(PushTemplate pushTemplate);
    void Delete(int templateid);
}
Run Code Online (Sandbox Code Playgroud)

And implementation

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)
    {
      // ... 
    }
}
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 7

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

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

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

消费范围服务托管服务

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

范围处理服务

//...

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

    //...
}
Run Code Online (Sandbox Code Playgroud)