【问题标题】:EFCore 2.0 : A second operation started on this context before a previous operation completedEFCore 2.0:在前一个操作完成之前在此上下文上启动了第二个操作
【发布时间】:2021-10-22 03:38:03
【问题描述】:

我明白错误在说什么,但无法实施解决方案。我需要在线程中调用的方法中调用数据库(只是为了选择)。

我已经为此搜索了多个答案并尝试添加ServiceLifetime.Transient,但得到了相同的结果。看起来我的 DI 出了点问题。

我也尝试将构造函数中的上下文传递给PriceSheetAsyncRepository,但没有运气。

public class PriceSheetRepository : IPriceSheetRepository
{
    private readonly Pricing_PortalContext _context;
    private readonly ILogger _logger;

    public PriceSheetRepository(Pricing_PortalContext context, ILogger<PriceSheetRepository> logger)
    {
        _context = context;
        _logger = logger;
    }
    public async Task<bool> ProcessPriceSheetImport(long ImportId)
    {
        var pricesheetAsyncRepo = new PriceSheetAsyncRepository(_logger);

        foreach (var i in RequestList)
        {
            tasks.Add(Task.Run(async () => await pricesheetAsyncRepo.ValidatePublishAsync(i, _context)));
        }
        await Task.WhenAll(tasks);
    }
}


public class PriceSheetAsyncRepository
{
    private readonly ILogger _logger;

    public PriceSheetAsyncRepository(ILogger logger)
    {
        _logger = logger;

    }

    public Task<bool> ValidatePublishAsync(PriceSheetImportModel i, MyDBContext _context)
    {
        return Task.Run(() =>
        {
            i.IsValid = true;
            i.LinePart = i.LinePart?.ToUpper();

            if (effDate != null && _context.MyTablePublish.Where(x => x.A == i.A).Any())
            {
                i.IsValid = false;
                i.Message = i.Message + "Duplicate published record, ";
            }
        }

     }
}

谁能指出我在这里做错了什么?

【问题讨论】:

  • 听起来实体框架没有线程安全上下文。

标签: .net-core dependency-injection entity-framework-core ef-core-2.0


【解决方案1】:

简单地说 - 实体框架上下文不是线程安全的,并且不允许多个操作并行运行。

如果您实际上有不同的上下文实例,则瞬态有效。但在您的情况下,您只是在多个并发任务上使用上下文。

要么使您的代码单线程(for 循环,内部等待,而不是添加到任务列表并使用 Task.WhenAll),要么为每个要使用它的任务创建一个新的上下文实例。

后者的示例可能如下所示(注意删除 Task.Run - 它伤害了你,没有帮助)

// in container builder
services.AddTransient<Pricing_PortalContext>();
services.AddTransient<Func<Pricing_PortalContext>>(sp => sp.GetRequiredService<Pricing_PortalContext>());

// inject a Func
public PriceSheetRepository(Func<Pricing_PortalContext> contextFactory, ILogger<PriceSheetRepository> logger)

// create a new func per task
tasks.Add(async () => await pricesheetAsyncRepo.ValidatePublishAsync(i, _contextFactory()));

【讨论】:

  • 谢谢@Jeff。如何为每个任务创建一个新的上下文实例。我累了几种方法,但没有运气。我需要更改 Context 类的实现吗?制作单线程存在我正在尝试优化的性能问题。
  • 您可以注册并注入一个 Func 并使用它为每个任务创建一个新的上下文,而不是注册和注入您的上下文。
【解决方案2】:

虽然您的问题有正确答案,但看起来您正试图用锤子杀死苍蝇。为你的问题召唤一堆任务是多余的。

public async Task ProcessPriceSheetImport(long ImportId)
{
    var ids = RequestList.Select(x => x.A);

    var existent = new HasSet<int>(await context.MyTablePublish.Where(x => ids.Contains(x.A)).Select(x => x.A).ToList());

    foreach (var i in RequestList)
    {
        if (existent.Contains(i.A))
        {
            i.IsValid = false;
            i.Message = i.Message + "Duplicate published record, ";
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 2021-04-11
    • 1970-01-01
    • 2022-01-01
    • 2020-01-22
    • 2018-11-07
    • 2020-03-13
    相关资源
    最近更新 更多