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

时间:2019-10-17 18:01:08

标签: c# .net-core entity-framework-core asp.net-core-hosted-services

我具有以下界面

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_1Name_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)
    {
      // ... 
    }
}

1 个答案:

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

    //...
}