除非完成后台任务,否则防止DbContext被处置

时间:2019-02-14 10:01:26

标签: c# asp.net-core dependency-injection entity-framework-core dbcontext

我运行了一个长时间运行的DbContext.SaveChanges()并尝试在排队的background task.中执行它,但是当代码访问Backgroundtask并且控制器内的请求被返回时,DbContext被处置并重新初始化。

总体我的问题(已更新,因为它似乎不是100%清楚-我的错!):

有没有一种方法可以防止在请求返回且Background-Task正在启动/运行时处理dbcontext?

更明确一点:当我向dbContext添加更改并添加/启动后台任务以保存此更改时,通过返回请求来处理dbContext。现在,后台任务尝试运行时,无需再保存任何更改,因为dbContext不再相同。

当我将Servicelifetime从Scoped更改为Singleton(DbContext和UnitOfWork)时,一切正常。但这不是解决方案:-/

第二个问题:如果我必须在后台任务中添加更改-向该任务传递参数的最佳方法是什么?

我已经在Startup.cs中注册了BackGroundTaskService和DbContext / UnitOfWork,并通过在Controller中进行依赖注入来捕获它。

启动代码部分:

        services.AddDbContext<MyDbContext>
            (options =>
            {
                options.UseSqlServer(connectionString);
            },
                ServiceLifetime.Scoped
            );

        services.AddScoped<IMyUnitOfWork, MyUnitOfWork>();

        // Background Services
        services.AddHostedService<QueuedHostedService>();
        services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();

BackgroundTask代码部分:

public class QueuedHostedService : BackgroundService
{
    private readonly ILogger _logger;
    private readonly IServiceScopeFactory _scopeFactory;

    public QueuedHostedService(IBackgroundTaskQueue taskQueue,ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory)
    {
        TaskQueue = taskQueue;
        _logger = loggerFactory.CreateLogger<QueuedHostedService>();
        _scopeFactory = scopeFactory;
    }

    public IBackgroundTaskQueue TaskQueue { get; }

    protected async override Task ExecuteAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Queued Hosted Service is starting.");

        while (!cancellationToken.IsCancellationRequested)
        {
            var workItem = await TaskQueue.DequeueAsync(cancellationToken);

            try
            {
                using (var scope = _scopeFactory.CreateScope())
                {
                    var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
                    await dbContext.SaveChangesAsync();
                    // the call workItem is not working -> object disposed
                    //await workItem(cancellationToken);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                   $"Error occurred executing {nameof(workItem)}.");
            }
        }

        _logger.LogInformation("Queued Hosted Service is stopping.");
    }
}

控制器代码部分:

_unitOfWork.Repository.AddRange(MyObjectList);
_queue.QueueBackgroundWorkItem(async token =>
                    {
                        await _unitOfWork.CompleteAsync();
                    });

UnitOfWork代码部分:

 public Task<int> CompleteAsync()
    {
        return _context.SaveChangesAsync();
    }

谢谢!

更新:

我之所以在后台任务中保存dbcontext更改的方法,是因为dbcontext必须插入80k行,并且ORM生成更多的insert-commands。所以我必须找到一种解决方案以使插入更加有效,而不是将问题隐藏在后台任务中。 流行语:ETL,SSIS

0 个答案:

没有答案