来自实体框架的 SqlException - 不允许新事务,因为会话中还有其他线程正在运行
我目前收到此错误:
System.Data.SqlClient.SqlException:不允许新事务,因为会话中还有其他线程正在运行。
运行此代码时:
public class ProductManager : IProductManager
{
#region Declare Models
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
#endregion
public IProduct GetProductById(Guid productId)
{
// Do a quick sync of the feeds...
SyncFeeds();
...
// get a product...
...
return product;
}
private void SyncFeeds()
{
bool found = false;
string feedSource = "AUTO";
switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
{
if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
{
var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
{
foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
{
if (targetProduct.alternateProductID == sourceProduct.AutoID)
{
found = true;
break;
}
}
if (!found)
{
var newProduct = new RivWorks.Model.Negotiation.Product();
newProduct.alternateProductID = sourceProduct.AutoID;
newProduct.isFromFeed = true;
newProduct.isDeleted = false;
newProduct.SKU = sourceProduct.StockNumber;
company.Product.Add(newProduct);
}
}
_dbRiv.SaveChanges(); // ### THIS BREAKS ### //
}
}
}
break;
}
}
}
模型#1 - 该模型位于我们的开发服务器上的数据库中。 模型 #1 http://content .screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
模型 #2 - 该模型位于我们的 Prod 服务器上的数据库中,并且是通过自动提要每天更新。 替代文本 http://content。 screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
注意 - 模型 #1 中的红色圆圈项目是我用来“映射”到模型#2。请忽略模型#2 中的红色圆圈:这是我遇到的另一个问题,现已得到解答。
注意:我仍然需要进行 isDeleted 检查,这样如果它已从我们客户的库存中消失,我可以将其从 DB1 中软删除。
我想要做的就是,使用这个特定的代码,将 DB1 中的一家公司与 DB2 中的客户端连接起来,从 DB2 获取他们的产品列表,然后将其插入到 DB1(如果尚不存在)。第一次通过应该是库存的全部拉出。每次运行时都不会发生任何事情,除非夜间有新的库存进入 Feed。
所以最大的问题 - 如何解决我遇到的交易错误?我是否需要每次循环时删除并重新创建上下文(对我来说没有意义)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
下面的代码对我有用:
The code below works for me:
我的情况和上面其他人的情况类似。我有一个 IQueryable,我正在对其进行 foreach 操作。这又调用了 SaveChanges() 方法。这里出现了异常,因为上面的查询已经打开了一个事务。
在我的例子中,将 ToList() 添加到查询末尾是解决方案。
My situation was similar others above. I had an IQueryable which I was doing a foreach on. This in turn called a method with SaveChanges(). Booom exception here as there was already a transaction open from the query above.
Adding ToList() to the end of the query was the solution in my case.
我参加聚会迟到了,但今天我遇到了同样的错误,解决方法很简单。我的场景与给定的代码类似,我在嵌套的 for-each 循环内进行数据库事务。
问题是,由于单个数据库事务比 for-each 循环花费的时间稍长,因此一旦较早的事务未完成,新的牵引就会抛出异常,因此解决方案是在 for-each 循环中创建一个新对象您正在进行数据库事务的位置。
对于上述场景,解决方案将是这样的:
I am much late to the party but today I faced the same error and how I resolved was simple. My scenario was similar to this given code I was making DB transactions inside of nested for-each loops.
The problem is as a Single DB transaction takes a little bit time longer than for-each loop so once the earlier transaction is not complete then the new traction throws an exception, so the solution is to create a new object in the for-each loop where you are making a db transaction.
For the above mentioned scenarios the solution will be like this:
我有点晚了,但我也遇到了这个错误。我通过检查更新的值解决了这个问题。
我发现我的查询是错误的,并且有超过 250 多个编辑待处理。所以我纠正了我的查询,现在它可以正常工作了。
希望这有助于解决未来的问题。
I am a little bit late, but I had this error too. I solved the problem by checking what where the values that where updating.
I found out that my query was wrong and that there where over 250+ edits pending. So I corrected my query, and now it works correct.
Hope this helps resolving future problems.
大多数答案都与循环有关。但我的问题不同。当我尝试在同一范围内使用多个 dbcontext.Savechanges() 命令时,我多次收到错误。
就我而言,对于 ef core 3.1,使用
dbcontext.Database.BeginTransaction()
和
dbcontext.Database.CommitTransaction();
已修复该问题。这是我的整个代码:
Most of answers related with loops. But my problem was different. While i was trying to use multiple dbcontext.Savechanges() command in same scope, i got the error many times.
In my case for ef core 3.1 using
dbcontext.Database.BeginTransaction()
and
dbcontext.Database.CommitTransaction();
has fixed the problem. Here is my entire Code :
在费尽心思之后,我发现
foreach
循环是罪魁祸首。需要做的是调用 EF,但将其返回到该目标类型的IList
中,然后在IList
上循环。例子:
After much pulling out of hair I discovered that the
foreach
loops were the culprits. What needs to happen is to call EF but return it into anIList<T>
of that target type then loop on theIList<T>
.Example:
正如您已经确定的,您无法在仍然通过活动读取器从数据库中绘制的
foreach
中进行保存。对于小型数据集,调用
ToList()
或ToArray()
没问题,但是当您有数千行时,您将消耗大量内存。最好以块的形式加载行。
给定上述扩展方法,您可以像这样编写查询:
调用此方法的可查询对象必须是有序的。这是因为实体框架仅支持
IQueryable.Skip( int)
在有序查询上,当您考虑到不同范围的多个查询需要稳定的顺序时,这是有意义的。如果排序对您来说不重要,只需按主键排序,因为它可能具有聚集索引。此版本将以 100 个为批次查询数据库。请注意,为每个实体调用
SaveChanges()
。如果您想显着提高吞吐量,则应减少调用
SaveChanges()
的频率。请改用如下代码:这会导致数据库更新调用减少 100 倍。当然,每个调用都需要更长的时间才能完成,但最终您仍然领先。你的里程可能会有所不同,但这对我来说是世界上更快的。
它绕过了您所看到的异常。
编辑 在运行 SQL Profiler 后我重新审视了这个问题,并更新了一些内容以提高性能。对于任何感兴趣的人,这里有一些示例 SQL,显示了数据库创建的内容。
第一个循环不需要跳过任何内容,因此更简单。
后续调用需要跳过之前的结果块,因此引入了
row_number
的用法:As you've already identified, you cannot save from within a
foreach
that is still drawing from the database via an active reader.Calling
ToList()
orToArray()
is fine for small data sets, but when you have thousands of rows, you will be consuming a large amount of memory.It's better to load the rows in chunks.
Given the above extension methods, you can write your query like this:
The queryable object you call this method on must be ordered. This is because Entity Framework only supports
IQueryable<T>.Skip(int)
on ordered queries, which makes sense when you consider that multiple queries for different ranges require the ordering to be stable. If the ordering isn't important to you, just order by primary key as that's likely to have a clustered index.This version will query the database in batches of 100. Note that
SaveChanges()
is called for each entity.If you want to improve your throughput dramatically, you should call
SaveChanges()
less frequently. Use code like this instead:This results in 100 times fewer database update calls. Of course each of those calls takes longer to complete, but you still come out way ahead in the end. Your mileage may vary, but this was worlds faster for me.
And it gets around the exception you were seeing.
EDIT I revisited this question after running SQL Profiler and updated a few things to improve performance. For anyone who is interested, here is some sample SQL that shows what is created by the DB.
The first loop doesn't need to skip anything, so is simpler.
Subsequent calls need to skip previous chunks of results, so introduces usage of
row_number
:我们现已发布对 Connect 上出现的错误。我们建议的解决方法如下:
此错误是由于实体框架在
SaveChanges()
调用期间创建隐式事务所致。解决该错误的最佳方法是使用不同的模式(即,在读取过程中不保存)或显式声明事务。以下是三种可能的解决方案:We have now posted an official response to the bug opened on Connect. The workarounds we recommend are as follows:
This error is due to Entity Framework creating an implicit transaction during the
SaveChanges()
call. The best way to work around the error is to use a different pattern (i.e., not saving while in the midst of reading) or by explicitly declaring a transaction. Here are three possible solutions:事实上,您无法使用实体框架在 C# 中的
foreach
循环内保存更改。context.SaveChanges()
方法的作用类似于常规数据库系统 (RDMS) 上的提交。只需进行所有更改(实体框架将缓存),然后在循环之后(在循环外部)调用 SaveChanges() 一次保存所有更改,就像数据库提交命令一样。
如果您可以一次保存所有更改,则此方法有效。
Indeed you cannot save changes inside a
foreach
loop in C# using Entity Framework.context.SaveChanges()
method acts like a commit on a regular database system (RDMS).Just make all changes (which Entity Framework will cache) and then save all of them at once calling
SaveChanges()
after the loop (outside of it), like a database commit command.This works if you can save all changes at once.
只需将
context.SaveChanges()
放在foreach
(循环)结束后即可。Just put
context.SaveChanges()
after end of yourforeach
(loop).将您的可查询列表设置为
.ToList()
,它应该可以正常工作。Make your queryable lists to
.ToList()
and it should work fine.仅供参考:来自一本书,并调整了一些行,因为它仍然有效:
调用
SaveChanges()
方法开始一个事务,如果在迭代完成之前发生异常,该事务会自动回滚保留到数据库的所有更改;否则事务提交。您可能会想在每次实体更新或删除之后应用该方法,而不是在迭代完成之后应用该方法,尤其是在更新或删除大量实体时。如果您在处理所有数据之前尝试调用
SaveChanges()
,则会出现“不允许新事务,因为会话中正在运行其他线程”异常。发生异常的原因是 SQL Server 不允许在打开了SqlDataReader
的连接上启动新事务,即使连接字符串启用了多个活动记录集 (MARS)(EF 的默认连接字符串启用了该功能)火星)有时最好了解事情发生的原因;-)
FYI: from a book and some lines adjusted because it's still valid:
Invoking
SaveChanges()
method begins a transaction which automatically rolls back all changes persisted to the database if an exception occurs before iteration completes; otherwise the transaction commits. You might be tempted to apply the method after each entity update or deletion rather than after iteration completes, especially when you're updating or deleting massive numbers of entities.If you try to invoke
SaveChanges()
before all data has been processed, you incur a "New transaction is not allowed because there are other threads running in the session" exception. The exception occurs because SQL Server doesn't permit starting a new transaction on a connection that has aSqlDataReader
open, even with Multiple Active Record Sets (MARS) enabled by the connection string (EF's default connection string enables MARS)Sometimes its better to understand why things are happening ;-)
始终将您的选择用作列表
,例如:
然后在保存更改时循环遍历集合
Always Use your selection as List
Eg:
Then Loop through the Collection while save changes
从 EF5 迁移到 EF6 后,我们开始看到此错误“不允许新事务,因为会话中还有其他线程正在运行”。
Google 将我们带到这里,但我们没有在循环内调用
SaveChanges()
。在从数据库读取数据的 foreach 循环中使用 ObjectContext.ExecuteFunction 执行存储过程时,会引发错误。对 ObjectContext.ExecuteFunction 的任何调用都会将该函数包装在事务中。在已经有打开的读取器的情况下开始事务会导致错误。
可以通过设置以下选项来禁用事务中的 SP 包装。
EnsureTransactionsForFunctionsAndCommands
选项允许 SP 在不创建自己的事务的情况下运行,并且不再引发错误。DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands 属性
We started seeing this error "New transaction is not allowed because there are other threads running in the session" after migrating from EF5 to EF6.
Google brought us here but we are not calling
SaveChanges()
inside the loop. The errors were raised when executing a stored procedure using the ObjectContext.ExecuteFunction inside a foreach loop reading from the DB.Any call to ObjectContext.ExecuteFunction wraps the function in a transaction. Beginning a transaction while there is already an open reader causes the error.
It is possible to disable wrapping the SP in a transaction by setting the following option.
The
EnsureTransactionsForFunctionsAndCommands
option allows the SP to run without creating its own transaction and the error is no longer raised.DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands Property
我遇到了同样的问题,但情况不同。我在列表框中有一个项目列表。用户可以单击一个项目并选择删除,但我使用存储过程来删除该项目,因为删除该项目涉及很多逻辑。当我调用存储过程时,删除工作正常,但以后对 SaveChanges 的任何调用都会导致错误。我的解决方案是在 EF 之外调用存储过程,这工作得很好。由于某种原因,当我使用 EF 方式调用存储过程时,它会留下一些开放的东西。
I was getting this same issue but in a different situation. I had a list of items in a list box. The user can click an item and select delete but I am using a stored proc to delete the item because there is a lot of logic involved in deleting the item. When I call the stored proc the delete works fine but any future call to SaveChanges will cause the error. My solution was to call the stored proc outside of EF and this worked fine. For some reason when I call the stored proc using the EF way of doing things it leaves something open.
这里还有另外 2 个选项,允许您在 foreach 循环中调用 SaveChanges()。
第一个选项是使用一个 DBContext 生成要迭代的列表对象,然后创建第二个 DBContext 来调用 SaveChanges() 。下面是一个示例:
第二个选项是从 DBContext 获取数据库对象列表,但仅选择 id。然后迭代 id 列表(大概是一个 int)并获取与每个 int 对应的对象,并以这种方式调用 SaveChanges() 。此方法背后的想法是获取一个大的整数列表,比获取一个大的 db 对象列表并在整个对象上调用 .ToList() 更有效。下面是该方法的一个示例:
Here are another 2 options that allow you to invoke SaveChanges() in a for each loop.
The first option is use one DBContext to generate your list objects to iterate through, and then create a 2nd DBContext to call SaveChanges() on. Here is an example:
The 2nd option is to get a list of database objects from the DBContext, but to select only the id's. And then iterate through the list of id's (presumably an int) and get the object corresponding to each int, and invoke SaveChanges() that way. The idea behind this method is grabbing a large list of integers, is a lot more efficient then getting a large list of db objects and calling .ToList() on the entire object. Here is an example of this method:
如果由于 foreach 而收到此错误,并且确实需要先在循环内保存一个实体,然后在循环中进一步使用生成的标识,就像我的情况一样,最简单的解决方案是使用另一个 DBContext 插入实体,该实体将返回 Id 并使用外部上下文中的此 Id
例如
If you get this error due to foreach and you really need to save one entity first inside loop and use generated identity further in loop, as was in my case, the easiest solution is to use another DBContext to insert entity which will return Id and use this Id in outer context
For example
我也面临着同样的问题。
下面是原因和解决方法。
http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx
确保在触发数据操作命令(如插入、更新)之前,您已经关闭了所有先前活动的 SQL 读取器。
最常见的错误是从数据库读取数据并返回值的函数。
例如像 isRecordExist 这样的函数。
在这种情况下,如果我们找到记录并且忘记关闭读取器,我们会立即从函数返回。
I was also facing same issue.
Here is the cause and solution.
http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx
Make sure before firing data manipulation commands like inserts, updates, you have closed all previous active SQL readers.
Most common error is functions that read data from db and return values.
For e.g functions like isRecordExist.
In this case we immediately return from the function if we found the record and forget to close the reader.
因此,在项目中,我遇到了完全相同的问题,问题不在
foreach
或.toList()
中,它实际上是在我们使用的 AutoFac 配置中。这造成了一些奇怪的情况,即抛出了上述错误,但也抛出了一堆其他等效的错误。
这是我们的修复:
将此更改
为:
So in the project were I had this exact same issue the problem wasn't in the
foreach
or the.toList()
it was actually in the AutoFac configuration we used.This created some weird situations were the above error was thrown but also a bunch of other equivalent errors were thrown.
This was our fix:
Changed this:
To:
就我而言,当我通过 EF 调用存储过程,然后 SaveChanges 抛出此异常时,问题就出现了。问题出在调用过程时,枚举器没有被释放。我按照以下方式修复了代码:
In my case, the problem appeared when I called Stored Procedure via EF and then later SaveChanges throw this exception. The problem was in calling the procedure, the enumerator was not disposed. I fixed the code following way:
我知道这是一个老问题,但我今天遇到了这个错误。
我发现,当数据库表触发器出错时,可能会抛出此错误。
作为参考,当您收到此错误时,您也可以检查表触发器。
I know it is an old question but i faced this error today.
and i found that, this error can be thrown when a database table trigger gets an error.
for your information, you can check your tables triggers too when you get this error.
我需要读取一个巨大的结果集并更新表中的一些记录。
我尝试按照 Drew Noakes 的 答案。
不幸的是,在 50000 条记录之后,我遇到了 OutofMemoryException。
答案实体框架大数据集,出于内存异常解释说,
建议为每个批次重新创建上下文。
因此,我检索了主键的最小值和最大值 - 表的主键作为自动增量整数。然后,我通过打开每个块的上下文从数据库中检索记录块。处理完块上下文后将关闭并释放内存。它确保内存使用量不会增加。
下面是我的代码片段:
FromToRange 是具有 From 和 To 属性的简单结构。
I needed to read a huge ResultSet and update some records in the table.
I tried to use chunks as suggested in Drew Noakes's answer.
Unfortunately after 50000 records I've got OutofMemoryException.
The answer Entity framework large data set, out of memory exception explains, that
The recommendation is to re-create your context for each batch.
So I've retrieved Minimal and Maximum values of the primary key- the tables have primary keys as auto incremental integers.Then I retrieved from the database chunks of records by opening context for each chunk. After processing the chunk context closes and releases the memory. It insures that memory usage is not growing.
Below is a snippet from my code:
FromToRange is a simple structure with From and To properties.
最近我在我的项目中遇到了同样的问题,所以发布我的经验,它可能会帮助一些和我一样的人。该问题是由于我循环访问 EF select 查询的结果(结果未检索到内存中)。
我已经更新了我的产品选择查询,将结果放入 LIST 而不是 IQueryable (这似乎为每个循环打开了阅读器,因此保存失败)。
Recently I faced the same issue in my project so posting my experience and it might help some on the same boat as i was. The issue was due to i am looping through the results of EF select query (results are not retrieved into memory).
I have updated my Products select query to bring the results into LIST rather than IQueryable (This seems to be opening the reader throughout for each loop and hence save was failing).