EFCore 2.0:在上一个操作完成之前,第二个操作在此上下文中启动
EFCore 2.0 : A second operation started on this context before a previous operation completed
我明白错误的意思,但无法实施解决方案。我需要在线程中调用的方法中调用数据库(仅 select)。
我为此搜索了多个答案并尝试添加 ServiceLifetime.Transient
但得到了相同的结果。看起来我的 DI 有问题。
我也试过将构造函数中的 Context 传递给 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, ";
}
}
}
}
任何人都可以指出我在这里做错了什么吗?
简而言之 - Entity Frameworks 上下文不是线程安全的,不允许多个操作并行 运行。
如果您实际上有不同的上下文实例,Transient 就可以工作。但在您的情况下,您只是在多个并发任务上使用上下文。
要么使您的代码成为单线程代码(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()));
虽然你的问题有正确答案,但看起来你正试图用锤子杀死苍蝇。为你的问题召集一堆任务是多余的。
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, ";
}
}
}
我明白错误的意思,但无法实施解决方案。我需要在线程中调用的方法中调用数据库(仅 select)。
我为此搜索了多个答案并尝试添加 ServiceLifetime.Transient
但得到了相同的结果。看起来我的 DI 有问题。
我也试过将构造函数中的 Context 传递给 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, ";
}
}
}
}
任何人都可以指出我在这里做错了什么吗?
简而言之 - Entity Frameworks 上下文不是线程安全的,不允许多个操作并行 运行。
如果您实际上有不同的上下文实例,Transient 就可以工作。但在您的情况下,您只是在多个并发任务上使用上下文。
要么使您的代码成为单线程代码(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()));
虽然你的问题有正确答案,但看起来你正试图用锤子杀死苍蝇。为你的问题召集一堆任务是多余的。
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, ";
}
}
}