Entity Framework Core:在前一个操作完成之前在此上下文上启动了第二个操作
Posted
技术标签:
【中文标题】Entity Framework Core:在前一个操作完成之前在此上下文上启动了第二个操作【英文标题】:Entity Framework Core: A second operation started on this context before a previous operation completed 【发布时间】:2018-07-23 21:19:09 【问题描述】:我正在使用 Entity Framework Core 开发一个 ASP.Net Core 2.0 项目
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0"/>
在我的一种列表方法中,我遇到了这个错误:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
这是我的方法:
[HttpGet("currentPage/pageSize/")]
[HttpGet("currentPage/pageSize/search")]
public ListResponseVM<ClientVM> GetClients([FromRoute] int currentPage, int pageSize, string search)
var resp = new ListResponseVM<ClientVM>();
var items = _context.Clients
.Include(i => i.Contacts)
.Include(i => i.Addresses)
.Include("ClientObjectives.Objective")
.Include(i => i.Urls)
.Include(i => i.Users)
.Where(p => string.IsNullOrEmpty(search) || p.CompanyName.Contains(search))
.OrderBy(p => p.CompanyName)
.ToPagedList(pageSize, currentPage);
resp.NumberOfPages = items.TotalPage;
foreach (var item in items)
var client = _mapper.Map<ClientVM>(item);
client.Addresses = new List<AddressVM>();
foreach (var addr in item.Addresses)
var address = _mapper.Map<AddressVM>(addr);
address.CountryCode = addr.CountryId;
client.Addresses.Add(address);
client.Contacts = item.Contacts.Select(p => _mapper.Map<ContactVM>(p)).ToList();
client.Urls = item.Urls.Select(p => _mapper.Map<ClientUrlVM>(p)).ToList();
client.Objectives = item.Objectives.Select(p => _mapper.Map<ObjectiveVM>(p)).ToList();
resp.Items.Add(client);
return resp;
我有点迷茫,尤其是当我在本地运行它时它可以工作,但是当我部署到我的登台服务器(IIS 8.5)时,它得到了这个错误并且它工作正常。在我增加其中一个模型的最大长度后,错误开始出现。我还更新了相应视图模型的最大长度。还有许多其他的列表方法非常相似,而且它们都在起作用。
我有一个 Hangfire 作业正在运行,但该作业不使用相同的实体。这就是我认为相关的所有内容。关于可能导致此问题的任何想法?
【问题讨论】:
检查this。 @Berkay 我看到了这个以及许多其他类似的问题并尝试了它们。我的方法是异步的,我让它同步以避免这些问题。我还尝试删除映射,也尝试删除 .ToPagedList 它继续抛出错误。 最好能看到完整的堆栈跟踪 并了解是否启用了多个活动结果 遇到了同样的问题,我发现我的数据库表中有可以为空的整数。一旦我将我的实体模型属性设置为匹配可为空的 int,它就开始工作了,这些消息对我来说是误导......! 【参考方案1】:我不确定您是否使用 IoC 和依赖注入来解析 DbContext 可能会用到的地方。如果您这样做并且您正在使用来自 .NET Core(或任何其他 IoC 容器)的本机 IoC,并且您收到此错误,请确保将您的 DbContext 注册为 Transient。做
services.AddDbContext<MyContext>(ServiceLifetime.Transient);
或
services.AddTransient<MyContext>();
而不是
services.AddDbContext<MyContext>();
AddDbContext 将上下文添加为作用域,这在使用多个线程时可能会导致麻烦。
当使用异步 lambda 表达式时,async / await operations 也可能导致此行为。
将其添加为瞬态也有其缺点。您将无法通过使用上下文的多个类对某些实体进行更改,因为每个类都将获得自己的 DbContext 实例。
对此的简单解释是,DbContext
实现不是线程安全的。你可以阅读更多关于这个here
【讨论】:
当我使用瞬态时,我得到以下连接错误(关闭或处置)'OmniService.DataAccess.Models.OmniServiceDbContext'。 System.ObjectDisposedException:无法访问已处置的对象。此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。 ...对象名称:'AsyncDisposer'。 嗨@大卫!我猜您正在使用Task.Run(async () => context.Set...)
而不等待它或创建范围数据库上下文而不等待结果。这意味着您的上下文在访问它时可能已经被释放。如果您使用的是 Microsoft DI,则必须自己在该 Task.Run
内创建一个依赖范围。也请查看这些链接。 ***.com/questions/45047877/…docs.microsoft.com/en-us/dotnet/api/…
如前所述,如果您错过了使用 await 关键字调用异步方法,您将面临这个问题。
这可能很好,但是对于数据访问的期望生命周期和解析范围,必须更加考虑周到,而不是在没有细微差别的情况下随意使用瞬态。事实上,我认为很少有人想要数据上下文瞬态。如果一个工作单元的范围涉及多个数据操作,那么事务范围应该不止于此。您的数据上下文的解析应该反映您的工作单元的范围。这是应该深思熟虑的事情,这不是一个万能的答案。
@alsami 你是我的英雄。 6 小时痛苦的调试。这就是解决方案。如果其他人将 IHttpContextAccessor 注入 DbContext 并且声明为空,这就是解决方案。非常感谢你。【参考方案2】:
在某些情况下,调用没有await
关键字的async方法会出现此错误,只需在方法调用前添加await
即可解决。但是,答案可能与提到的问题无关,但它可以帮助解决类似的错误。
【讨论】:
这发生在我身上。将First()
更改为 await / FirstAsync()
有效。
点赞。另请参阅jimlynn.wordpress.com/2017/11/16/…Jim Lynn“实体框架错误:在上一个操作完成之前,在此上下文上开始了第二个操作。不保证任何实例成员都是线程安全的。”
谢谢!这正是我的问题...忘记在异步方法上添加等待。
也发生在我身上,这条评论帮助我搜索了忘记丢失等待的地方。一旦我找到它,问题就解决了。
使用 Resharper “错误/警告窗口”,您可以在全球范围内快速搜索此类问题【参考方案3】:
异常表示_context
同时被两个线程使用;两个线程在同一个请求中,或者由两个请求。
您的_context
是否声明为静态?不应该。
或者您是否从代码中的其他位置在同一个请求中多次调用GetClients
?
您可能已经这样做了,但理想情况下,您将使用 dependency injection 作为您的 DbContext
,这意味着您将在 Startup.cs 中使用 AddDbContext()
,并且您的控制器构造函数看起来会有所不同像这样:
private readonly MyDbContext _context; //not static
public MyController(MyDbContext context)
_context = context;
如果您的代码不是这样,请向我们展示,也许我们可以提供进一步帮助。
【讨论】:
可能这就是我的工作。我设法解决了,请参阅我的答案。但我将你的标记为正确的 @NMathur 您是否在其他线程中使用您的_context
对象?比如在Task.Run()
里面?
@NMathur 看起来不错。只需确保您始终将 await
与异步方法一起使用。如果不使用await
,可能会无意中进入多线程。【参考方案4】:
我遇到了同样的问题,结果发现 父服务是一个单例。所以上下文也自动变成了单例。尽管在 DI 中被声明为 Per Life Time Scoped。
将不同生命周期的服务注入另一个
切勿将 Scoped & Transient 服务注入 Singleton 服务。 ( 这有效地将瞬态或范围服务转换为 单身人士。 )
永远不要将瞬态服务注入作用域服务(这会将 临时服务进入作用域。 )
【讨论】:
这正是我的问题 这也是我的问题。我将一个处理程序类注册为单例,并将 DbContext 注册为瞬态。每次点击 Handler 时,我都必须在 Handler 类中使用 ServiceProvider 从 DI 容器中获取瞬态实例 这是对您将遇到此异常的最常见情况的非常简明的解释,尤其是在您刚刚开始的情况下。 我的父服务是一个范围服务,我正在向它注入一个临时服务。也将其设为瞬态后,它工作正常。但我仍然需要了解拥有临时服务的含义。谢谢! 我正在注册一个返回作用域实例的单例工厂。也使工厂范围扩大,问题就消失了。【参考方案5】:使用我的 Startup.cs 文件中的这行代码解决我的问题。 添加临时服务意味着每次请求服务时,都会在您工作时创建一个新实例 和 依赖注入
services.AddDbContext<Context>(options =>
options.UseSqlServer(_configuration.GetConnectionString("ContextConn")),
ServiceLifetime.Transient);
【讨论】:
这种方法在多用户即时交易数量较多的情况下会出现问题。 这正是我在帖子中所说的。【参考方案6】:我认为这个答案仍然可以帮助一些人并节省很多次。我通过将IQueryable
更改为List
(或数组、集合...)解决了类似的问题。
例如:
var list = _context.table1.Where(...);
到
var list = _context.table1.Where(...).ToList(); //or ToArray()...
【讨论】:
恕我直言,这个答案不值得减分,只是表达不佳。 .ToList() 确实解决了“第二次操作......”的大多数问题,因为它强制立即评估表达式。这样就没有排队上下文操作。 这是我的问题。我在查询的 where 子句中有 xxx.Contains(z.prop)。 xxx 应该是从早期查询解析的不同 int[] 数组。不幸的是,当第二个查询命中时,xxx 仍然是一个 IQueryable。在第二个查询之前添加 xxx.ToArray() 解决了我的问题。【参考方案7】:我有同样的错误。发生这种情况是因为我调用了一个构造为public async void ...
而不是public async Task ...
的方法。
【讨论】:
对我来说也是这样 :)【参考方案8】:Entity Framework Core 不支持在同一个 DbContext
实例上运行多个并行操作。这包括async
查询的并行执行和来自多个线程的任何显式并发使用。因此,请始终立即调用await async
,或者为并行执行的操作使用单独的DbContext
实例。
【讨论】:
对我来说就是这样。我终于发现在 FirstOrDefaultAsync(..) 调用之后我忘记调用 await 了。奇怪的是它在Debug模式下工作,但在发布模式下失败。【参考方案9】:我遇到了同样的问题,但原因不是上面列出的任何一个。我创建了一个任务,在任务内部创建了一个范围,并要求容器获取服务。这工作得很好,但后来我在任务中使用了第二个服务,我忘了也要求它到新的范围。因此,第二个服务使用了一个已经被释放的 DbContext。
Task task = Task.Run(() =>
using (var scope = serviceScopeFactory.CreateScope())
var otherOfferService = scope.ServiceProvider.GetService<IOfferService>();
// everything was ok here. then I did:
productService.DoSomething(); // (from the main scope) and this failed because the db context associated to that service was already disposed.
...
我应该这样做:
var otherProductService = scope.ServiceProvider.GetService<IProductService>();
otherProductService.DoSomething();
【讨论】:
只有在 using 块中的所有内容都完成执行后才会公开上下文吗? 当动作被释放时,所有的东西都被释放在那个范围内。如果您有一个在后台运行的任务并且该任务比操作更长,那么您将遇到此问题,除非您为该任务创建一个新的范围,就像我在示例中所做的那样。另一方面,如果您的任务可能需要很长时间,或者您希望 100% 确定它会运行,您可能需要使用队列。如果您使用的是 Azure,则可以使用服务总线队列。【参考方案10】:我有一个后台服务,它为表中的每个条目执行一个操作。问题是,如果我在 DbContext 的同一实例上迭代并修改所有数据,则会发生此错误。
一个解决方案,正如这个线程中提到的,通过定义它来将 DbContext 的生命周期更改为瞬态
services.AddDbContext<DbContext>(ServiceLifetime.Transient);
但是因为我在多个不同的服务中进行了更改并使用SaveChanges()
方法一次提交它们,所以这个解决方案在我的情况下不起作用。
因为我的代码在服务中运行,所以我正在做类似的事情
using (var scope = Services.CreateScope())
var entities = scope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
var writeService = scope.ServiceProvider.GetRequiredService<IWriteService>();
foreach (Entity entity in entities)
writeService.DoSomething(entity);
能够像使用简单请求一样使用服务。所以为了解决这个问题,我只是将单个范围分成两部分,一个用于查询,另一个用于写操作,如下所示:
using (var readScope = Services.CreateScope())
using (var writeScope = Services.CreateScope())
var entities = readScope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
var writeService = writeScope.ServiceProvider.GetRequiredService<IWriteService>();
foreach (Entity entity in entities)
writeService.DoSomething(entity);
像这样,有效地使用了两个不同的 DbContext 实例。
另一种可能的解决方案是确保读取操作在开始迭代之前已终止。这在我的情况下不是很实用,因为可能有很多结果都需要加载到内存中以进行操作,而我首先尝试通过使用 Queryable 来避免。
【讨论】:
【参考方案11】:如果您创建了迁移 (Add-Migration) 但忘记实际更新数据库 (Update-Database),也可能会出现此错误。
【讨论】:
您所描述的是未正确配置的 EF 运行时的另一个潜在症状。【参考方案12】:有一些函数 Async 但你不要在它之前调用 await 。在调用该函数之前查找并添加 await 将对您有所帮助。
【讨论】:
【参考方案13】:我知道两年前有人问过这个问题,但我刚遇到这个问题,我使用的修复程序确实有帮助。
如果您使用相同的上下文执行两个查询 - 您可能需要删除 AsNoTracking
。如果您确实使用AsNoTracking
,您将为每次读取创建一个新的数据读取器。两个数据读取器不能读取相同的数据。
【讨论】:
DataReader
上的好皮卡,但可能会出现其他例外情况,这个建议仍然只是一种解决方法。您不应该尝试同时针对同一个上下文执行两个查询,如果需要,只需创建一个新的隔离上下文。【参考方案14】:
我收到了同样的信息。但这对我来说没有任何意义。我的问题是我错误地使用了“NotMapped”属性。 在某些情况下,它可能仅意味着 Linq 语法或模型类的错误。错误消息似乎具有误导性。这条消息的原意是你不能在同一个请求中对同一个 dbcontext 多次调用 async。
[NotMapped]
public int PostId get; set;
public virtual Post Post get; set;
您可以查看此链接了解详细信息, https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-this-context-before-a-previous-operation-completed
【讨论】:
【参考方案15】:当我尝试在下面代码的异步方法中使用FirstOrDefaultAsync()
时遇到了同样的问题。当我修复FirstOrDefault()
时,问题就解决了!
_context.Issues.Add(issue);
await _context.SaveChangesAsync();
int userId = _context.Users
.Where(u => u.UserName == Options.UserName)
.FirstOrDefaultAsync()
.Id;
...
【讨论】:
和 FirstOrDefault() 或者 FirstOrDefaultAsync() 完全没有关系,是关于 dbContext 的使用。 其实是有关系的。我设法解决了攻击正是这个原因。但在我的情况下,它正在为 SaveChanges 更改 SaveChangesAsync。出于某种原因,上下文允许使用异步进行其他执行。【参考方案16】:我通过将IQueryable
传递给一个方法,然后将该 IQueryable“列表”用作对同一上下文的另一个查询的一部分,设法得到了该错误。
public void FirstMethod()
// This is returning an IQueryable
var stockItems = _dbContext.StockItems
.Where(st => st.IsSomething);
SecondMethod(stockItems);
public void SecondMethod(IEnumerable<Stock> stockItems)
var grnTrans = _dbContext.InvoiceLines
.Where(il => stockItems.Contains(il.StockItem))
.ToList();
为了阻止这种情况发生,我使用了approach here 并在传递第二种方法之前将该列表具体化,方法是将对SecondMethod
的调用更改为SecondMethod(stockItems.ToList()
【讨论】:
这解决了问题,但这不会降低性能,有没有其他解决方案?【参考方案17】:我的情况不同: 我试图用 30 个属于特定角色的用户为数据库播种,所以我运行了以下代码:
for (var i = 1; i <= 30; i++)
CreateUserWithRole("Analyst", $"analysti", UserManager);
这是一个同步功能。 在里面我有 3 个电话:
UserManager.FindByNameAsync(username).Result
UserManager.CreateAsync(user, pass).Result
UserManager.AddToRoleAsync(user, roleName).Result
当我用.GetAwaiter().GetResult()
替换.Result
时,这个错误就消失了。
【讨论】:
相反,您应该使用await
关键字,而不是试图强制每个异步操作同步运行。【参考方案18】:
首先,(至少)支持 alsami 的回答。这让我走上了正确的道路。
但对于那些从事 IoC 的人来说,这里有一点更深入的了解。
我的错误(和其他人一样)
发生了一个或多个错误。 (第二次手术开始于这个 前一个操作完成之前的上下文。这通常是由于 由不同的线程使用相同的 DbContext 实例。更多 有关如何避免 DbContext 线程问题的信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2097913.)
我的代码设置。 “只是基础”...
public class MyCoolDbContext: DbContext
public DbSet <MySpecialObject> MySpecialObjects get; set;
和
public interface IMySpecialObjectDomainData
并且(注意 MyCoolDbContext 正在被注入)
public class MySpecialObjectEntityFrameworkDomainDataLayer: IMySpecialObjectDomainData
public MySpecialObjectEntityFrameworkDomainDataLayer(MyCoolDbContext context)
/* HERE IS WHERE TO SET THE BREAK POINT, HOW MANY TIMES IS THIS RUNNING??? */
this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
和
public interface IMySpecialObjectManager
和
public class MySpecialObjectManager: IMySpecialObjectManager
public const string ErrorMessageIMySpecialObjectDomainDataIsNull = "IMySpecialObjectDomainData is null";
private readonly IMySpecialObjectDomainData mySpecialObjectDomainData;
public MySpecialObjectManager(IMySpecialObjectDomainData mySpecialObjectDomainData)
this.mySpecialObjectDomainData = mySpecialObjectDomainData ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectDomainDataIsNull, (Exception)null);
最后,我的多线程类,从控制台应用程序(命令行界面应用程序)调用
public interface IMySpecialObjectThatSpawnsThreads
和
public class MySpecialObjectThatSpawnsThreads: IMySpecialObjectThatSpawnsThreads
public const string ErrorMessageIMySpecialObjectManagerIsNull = "IMySpecialObjectManager is null";
private readonly IMySpecialObjectManager mySpecialObjectManager;
public MySpecialObjectThatSpawnsThreads(IMySpecialObjectManager mySpecialObjectManager)
this.mySpecialObjectManager = mySpecialObjectManager ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectManagerIsNull, (Exception)null);
和 DI 积累。 (同样,这是针对控制台应用程序(命令行界面)...其行为与 Web 应用程序略有不同)
private static IServiceProvider BuildDi(IConfiguration configuration)
/* this is being called early inside my command line application ("console application") */
string defaultConnectionStringValue = string.Empty; /* get this value from configuration */
////setup our DI
IServiceCollection servColl = new ServiceCollection()
////.AddLogging(loggingBuilder => loggingBuilder.AddConsole())
/* THE BELOW TWO ARE THE ONES THAT TRIPPED ME UP. */
.AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
.AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()
/* so the "ServiceLifetime.Transient" below................is what you will find most commonly on the internet search results */
# if (MY_ORACLE)
.AddDbContext<ProvisioningDbContext>(options => options.UseOracle(defaultConnectionStringValue), ServiceLifetime.Transient);
# endif
# if (MY_SQL_SERVER)
.AddDbContext<ProvisioningDbContext>(options => options.UseSqlServer(defaultConnectionStringValue), ServiceLifetime.Transient);
# endif
servColl.AddSingleton <IMySpecialObjectThatSpawnsThreads, MySpecialObjectThatSpawnsThreads>();
ServiceProvider servProv = servColl.BuildServiceProvider();
return servProv;
让我感到惊讶的是(更改为)瞬态
.AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
.AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()
注意,我认为因为 IMySpecialObjectManager 被注入到“MySpecialObjectThatSpawnsThreads”中,所以那些注入的对象需要是瞬态的才能完成链。
重点是....... 需要 .Transient 的不仅仅是 (My)DbContext...而是更大的 DI 图表块。
调试提示:
这一行:
this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
把你的调试器断点放在那里。如果您的 MySpecialObjectThatSpawnsThreads 正在创建 N 个线程(例如 10 个线程)......并且该行只被击中一次......那是你的问题。您的 DbContext 正在跨线程。
奖金:
我建议阅读下面的 url/article(oldie but goodie),了解 web-apps 和 console-apps 的区别
https://mehdi.me/ambient-dbcontext-in-ef6/
这是文章的标题,以防链接更改。
使用实体框架 6 以正确的方式管理 DBCONTEXT:深入探讨 指南 Mehdi El Gueddari
我用 WorkFlowCore https://github.com/danielgerlag/workflow-core 解决了这个问题
<ItemGroup>
<PackageReference Include="WorkflowCore" Version="3.1.5" />
</ItemGroup>
下面的示例代码.. 以帮助未来的互联网搜索者
namespace MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Workflows
using System;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Constants;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Glue;
using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.WorkflowSteps;
using WorkflowCore.Interface;
using WorkflowCore.Models;
public class MySpecialObjectInterviewDefaultWorkflow : IWorkflow<MySpecialObjectInterviewPassThroughData>
public const string WorkFlowId = "MySpecialObjectInterviewWorkflowId";
public const int WorkFlowVersion = 1;
public string Id => WorkFlowId;
public int Version => WorkFlowVersion;
public void Build(IWorkflowBuilder<MySpecialObjectInterviewPassThroughData> builder)
builder
.StartWith(context =>
Console.WriteLine("Starting workflow...");
return ExecutionResult.Next();
)
/* bunch of other Steps here that were using IMySpecialObjectManager.. here is where my DbContext was getting cross-threaded */
.Then(lastContext =>
Console.WriteLine();
bool wroteConcreteMsg = false;
if (null != lastContext && null != lastContext.Workflow && null != lastContext.Workflow.Data)
MySpecialObjectInterviewPassThroughData castItem = lastContext.Workflow.Data as MySpecialObjectInterviewPassThroughData;
if (null != castItem)
Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete :) 0 -> 1", castItem.PropertyOne, castItem.PropertyTwo);
wroteConcreteMsg = true;
if (!wroteConcreteMsg)
Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete (.Data did not cast)");
return ExecutionResult.Next();
))
.OnError(WorkflowCore.Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(60));
和
ICollection<string> workFlowGeneratedIds = new List<string>();
for (int i = 0; i < 10; i++)
MySpecialObjectInterviewPassThroughData currentMySpecialObjectInterviewPassThroughData = new MySpecialObjectInterviewPassThroughData();
currentMySpecialObjectInterviewPassThroughData.MySpecialObjectInterviewPassThroughDataSurrogateKey = i;
//// private readonly IWorkflowHost workflowHost;
string wfid = await this.workflowHost.StartWorkflow(MySpecialObjectInterviewDefaultWorkflow.WorkFlowId, MySpecialObjectInterviewDefaultWorkflow.WorkFlowVersion, currentMySpecialObjectInterviewPassThroughData);
workFlowGeneratedIds.Add(wfid);
【讨论】:
我对你投了反对票,因为你的变量和类型命名特别白痴。它们使您的整个帖子非常难以阅读。【参考方案19】:在我的例子中,我在 Blazor 中使用模板组件。
<BTable ID="Table1" TotalRows="MyList.Count()">
问题是在组件头中调用了一个方法(Count)。 为了解决这个问题,我改变了它:
int total = MyList.Count();
及以后:
<BTable ID="Table1" TotalRows="total">
【讨论】:
【参考方案20】:我之前遇到过这个问题。问题是异步/等待操作。我做了一个异步函数。此函数在运行时设置数据库的连接字符串。我做了这个函数异步函数。所以,我去掉了这个函数的 async/await 操作,问题就解决了。 使数据库瞬态可以解决问题,但如果您需要更新数据库中许多表的数据,那就不好了。
【讨论】:
【参考方案21】:这发生在我身上。 因为方法是异步的,所以我用 await 问题解决了。
【讨论】:
【参考方案22】:为这个错误添加另一个可能的解决方案,以防它帮助某人。
在我的情况下,问题是在查询中使用导航属性,如下所示:
var selectedOrder = dbContext.Orders.Where(x => x.Id == id).Single();
var relatedOrders = dbContext.Orders.Where(x => x.User.Id == selectedOrder.User.Id).ToList();
问题是在查询中使用 selectedOrder.User.Id。如果尚未加载 User nav 属性,EF 将尝试在尝试执行查询的过程中延迟加载该属性,它认为这是在尝试启动第二个操作。解决方案是为 selectedOrder.User.Id 创建一个单独的变量,以确保在查询开始之前加载查询所需的信息:
var selectedOrder = dbContext.Orders.Where(x => x.Id == id).Single();
var userId = selectedOrder.User.Id;
var relatedOrders = dbContext.Orders.Where(x => x.User.Id == userId).ToList();
【讨论】:
【参考方案23】:在我的情况下,我使用的锁不允许使用 await,并且当您不等待异步时不会创建编译器警告。
问题:
lock (someLockObject)
// do stuff
context.SaveChangesAsync();
// some other code somewhere else doing await context.SaveChangesAsync() shortly after the lock gets the concurrency error
修复: 通过使用 .Wait() 阻塞来等待锁内的异步
lock (someLockObject)
// do stuff
context.SaveChangesAsync().Wait();
【讨论】:
您应该避免在DbContext
周围锁定。这是一种反模式,它表明您应该解决其他结构性问题。 DbContext
应该代表一个工作单元,你应该尽量避免多个并发进程首先尝试使用相同的上下文。
如果我没记错的话,锁是批处理作业中的额外保护,以避免意外的双重运行,例如发电子邮件。如果批处理作业调度程序设法多次启动同一个作业(我们已经发生过这种情况,不止一次)
在这种情况下,每个批次都应该有自己的上下文实例。批处理毕竟是工作单元。我不是说没有地方放锁,而是说围绕保存更改是错误的地方。在回读时,我现在看到您的意思是您正在锁定一个以保存更改结尾的进程......是的,我只是以不同的方式构建,这个错误只是您在使用时必须解决的许多症状之一像这样的锁定模式。【参考方案24】:
另一种可能的情况:如果直接使用连接,不要忘记关闭 if。我需要执行任意 SQL 查询,并读取结果。这是一个快速修复,我不想定义数据类,也不想设置“正常”SQL 连接。所以很简单,我将 EFC 的数据库连接重用为var connection = Context.Database.GetDbConnection() as SqlConnection
。确保在拨打Context.SaveChanges()
之前拨打connection.Close()
。
【讨论】:
【参考方案25】:我尝试了上述所有解决方案,但没有一个对我运行代码的方式有帮助。然后
在使用 this._context...
之前添加 Task.Delay(300).Wait();
解决了这个问题,因为它在访问上下文之前等待 300 毫秒,从而允许完成之前的操作。
最终代码如下所示
Task.Delay(300).Wait();
response.Item = await this._context.Users
.Where(x => x.Username.ToLower().Equals(email))
.FirstOrDefaultAsync();
【讨论】:
这绝不是一个好主意。作为答案,它毫无意义,因为这完全是您自己的个人案例,我们不知道。修复错误的根源,这是一个在多个线程中使用的上下文。【参考方案26】:如果你的方法返回了一些东西,你可以通过把
.Result
到工作结束和
.Wait()
如果它没有返回任何东西。
【讨论】:
【参考方案27】:您可以使用 SemaphoreSlim 阻止将尝试执行该 EF 调用的下一个线程。
static SemaphoreSlim semSlim = new SemaphoreSlim(1, 1);
await semSlim.WaitAsync();
try
// something like this here...
// EmployeeService.GetList(); or...
var result = await _ctx.Employees.ToListAsync();
finally
semSlim.Release();
【讨论】:
请不要。即使在串行处理中,与多个线程共享上下文实例从来没有是一个好主意。【参考方案28】:我只是设法让它再次工作。这没有多大意义,但确实有效:
-
从 StartUp 中删除 Hangfire(我正在那里创建工作)
已删除hangfire 数据库
重新启动服务器
稍后我会进一步调查,但我使用 hangfire 调用的方法收到了 DBContext,这就是可能的原因。
【讨论】:
以上是关于Entity Framework Core:在前一个操作完成之前在此上下文上启动了第二个操作的主要内容,如果未能解决你的问题,请参考以下文章
EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?