来自实体框架的 SqlException - 不允许新事务,因为会话中正在运行其他线程

Posted

技术标签:

【中文标题】来自实体框架的 SqlException - 不允许新事务,因为会话中正在运行其他线程【英文标题】:SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session 【发布时间】:2011-01-07 23:34:08 【问题描述】:

我目前收到此错误:

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 - 此模型位于我们开发服务器上的数据库中。 Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png

模型 #2 - 此模型位于我们的产品服务器上的数据库中,并且每天通过自动提要进行更新。 alt text 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 中(如果它还没有的话)。第一次通过应该是一个完整的库存。每次它在那里运行时都不会发生任何事情,除非新的库存在一夜之间进入。

那么大问题 - 我如何解决我收到的交易错误?我是否需要在每次循环中删除并重新创建我的上下文(对我来说没有意义)?

【问题讨论】:

这是我见过的最详细的问题。 有人错过存储过程了吗? 【参考方案1】:

在大量拔掉头发后,我发现foreach 循环是罪魁祸首。需要做的是调用 EF,但将其返回到该目标类型的 IList<T>,然后在 IList<T> 上循环。

例子:

IList<Client> 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;
    // ...

【讨论】:

是的,这也让我很头疼。当我发现问题时,我差点从椅子上摔下来!我了解问题背后的技术原因,但这并不直观,也无助于开发人员陷入“成功之坑”blogs.msdn.com/brada/archive/2003/10/02/50420.aspx 大型数据集的性能不是很糟糕吗?如果表中有数百万条记录。 ToList() 会将它们全部吸入内存。我遇到了这个问题,想知道以下是否可行 a) 分离实体 b) 创建一个新的 ObjectContext 并将分离的实体附加到它。 c) 在新的 ObjectContext 上调用 SaveChanges() d) 从新的 ObjectContext 中分离实体 e) 将其附加回旧的 ObjectContext 问题是当您仍在从数据库中提取结果时,您无法调用SaveChanges。因此,另一种解决方案是在循环完成后保存更改。 也被咬了,我把它添加到 Microsoft Connect:connect.microsoft.com/VisualStudio/feedback/details/612369/… 随意投票。 我们的开发人员倾向于将 .ToList() 附加到任何 LINQ 查询而不考虑后果。这一定是第一次附加 .ToList() 真的很有用!【参考方案2】:

正如您已经确定的那样,您无法从仍在通过活动阅读器从数据库中绘制的foreach 中保存。

调用ToList()ToArray() 对于小型数据集很好,但是当您有数千行时,您将消耗大量内存。

最好分块加载行。

public static class EntityFrameworkUtil

    public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
    
        return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
    

    public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
    
        int chunkNumber = 0;
        while (true)
        
            var query = (chunkNumber == 0)
                ? queryable 
                : queryable.Skip(chunkNumber * chunkSize);
            var chunk = query.Take(chunkSize).ToArray();
            if (chunk.Length == 0)
                yield break;
            yield return chunk;
            chunkNumber++;
        
    

鉴于上述扩展方法,您可以这样编写查询:

foreach (var client in clientList.OrderBy(c => c.Id).QueryInChunksOf(100))

    // do stuff
    context.SaveChanges();

您调用此方法的可查询对象必须是有序的。 这是因为 Entity Framework 仅支持有序查询上的IQueryable&lt;T&gt;.Skip(int),当您认为针对不同范围的多个查询需要订购是稳定的。如果排序对您来说不重要,只需按主键排序,因为这可能具有聚集索引。

这个版本会以100个为单位批量查询数据库。注意每个实体都会调用SaveChanges()

如果您想显着提高吞吐量,您应该减少拨打SaveChanges() 的频率。改用这样的代码:

foreach (var chunk in clientList.OrderBy(c => c.Id).QueryChunksOfSize(100))

    foreach (var client in chunk)
    
        // do stuff
    
    context.SaveChanges();

这导致数据库更新调用减少 100 倍。当然,这些电话中的每一个都需要更长的时间才能完成,但最终你仍然遥遥领先。你的里程可能会有所不同,但这对我来说是世界上更快的。

它绕过了您看到的异常。

编辑我在运行 SQL Profiler 后重新审视了这个问题,并更新了一些内容以提高性能。对于任何感兴趣的人,这里有一些示例 SQL,显示了 DB 创建的内容。

第一个循环不需要跳过任何东西,所以更简单。

SELECT TOP (100)                     -- the chunk size 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Clients] AS [Extent1]
ORDER BY [Extent1].[Id] ASC

后续调用需要跳过之前的结果块,所以引入row_number的用法:

SELECT TOP (100)                     -- the chunk size
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM (
    SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], row_number()
    OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
    FROM [dbo].[Clients] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 100   -- the number of rows to skip
ORDER BY [Extent1].[Id] ASC

【讨论】:

谢谢。您的解释比标记为“已回答”的解释有用得多。 这很棒。只有一件事:如果您正在查询列并更新该列的值,则需要注意 chunkNumber++; .假设您有一列“ModifiedDate”并且您正在查询 .Where(x=> x.ModifiedDate != null),并且在 foreach 结束时为 ModifiedDate 设置了一个值。通过这种方式,您不会迭代一半的记录,因为有一半的记录被跳过了。 不幸的是,在庞大的数据集上,您会遇到 OutofMemoryException - 请参阅 Entity framework large data set, out of memory exception 中的说明。我已经在SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session 中描述了如何在每批中更新您的上下文 我认为这应该可行。变种跳过= 0;常量 int 取 = 100;列表 emps ; while ((emps = db.Employees.Skip(skip).Take(take).ToList()).Count > 0) skip += take; foreach (var emp in emps) // Do stuff here 我会制定一个答案,但它会被埋在下面的一堆答案之下,并且与这个问题有关。 谢谢!在我的情况下,“foreach”之后的“SaveChange”是一个简单的解决方案:)【参考方案3】:

我们现已向the bug opened on Connect 发布了官方回复。我们推荐的解决方法如下:

此错误是由于实体框架在 SaveChanges() 调用期间创建了隐式事务。解决该错误的最佳方法是使用不同的模式(即,在读取过程中不保存)或通过显式声明事务。以下是三种可能的解决方案:

// 1: Save after iteration (recommended approach in most cases)
using (var context = new MyContext())

    foreach (var person in context.People)
    
        // Change to person
    
    context.SaveChanges();


// 2: Declare an explicit transaction
using (var transaction = new TransactionScope())

    using (var context = new MyContext())
    
        foreach (var person in context.People)
        
            // Change to person
            context.SaveChanges();
        
    
    transaction.Complete();


// 3: Read rows ahead (Dangerous!)
using (var context = new MyContext())

    var people = context.People.ToList(); // Note that this forces the database
                                          // to evaluate the query immediately
                                          // and could be very bad for large tables.

    foreach (var person in people)
    
        // Change to person
        context.SaveChanges();
    
 

【讨论】:

如果您采用 Transaction 路线,仅投入 TransactionScope 可能无法修复它 - 如果您正在做的事情可能需要很长时间,请不要忘记延长 Timeout - 例如,如果您将交互式地调试进行 DB 调用的代码。这是将事务超时延长到一小时的代码: using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0))) 我第一次从“教程路径”脱离到我自己的真实示例时遇到了这个错误!然而,对我来说,更简单的解决方案,迭代后保存,更好! (我认为 99% 的情况是这样,只有 1% 的人真正必须在循环内执行数据库保存) 毛。我刚刚遇到了这个错误。非常讨厌。第二个建议对我来说就像一个魅力,同时将我的 SaveChanges 移动到循环中。我认为在循环之外保存更改更适合批量更改。不过没关系。我猜不会?! :( 不适用于我 .NET 4.5。使用 TransactionScope 时,我收到以下错误“EnlistTransaction 上的基础提供程序失败。”合作伙伴事务管理器已禁用其对远程/网络事务的支持。 (来自 HRESULT 的异常:0x8004D025)“”。我最终在迭代之外完成了这项工作。 使用 TransactionScope 是危险的,因为在整个事务期间表被锁定。【参考方案4】:

确实,您无法使用实体框架在 C# 中的 foreach 循环内保存更改。

context.SaveChanges() 方法的作用类似于常规数据库系统 (RDMS) 上的提交。

只需进行所有更改(Entity Framework 将缓存),然后在循环之后(在循环之外)调用SaveChanges() 一次保存所有更改,就像数据库提交命令一样。

如果您可以一次保存所有更改,则此方法有效。

【讨论】:

我觉得在这里看到“常规数据库系统 (RDMS)”很有趣 这似乎是错误的,因为在 EF 中 90% 的上下文中重复调用 SaveChanges 都可以。 似乎重复调用 SaveChanges 没问题,除非 foreach 循环正在迭代一个 db 实体。 啊哈!在 for-each 循环中引入上下文! (噗……我在想什么?……)谢谢!【参考方案5】:

只需将context.SaveChanges() 放在foreach(循环)的末尾即可。

【讨论】:

这是我在我的案例中发现的更好的选择,因为它保存在 foreach 中 这并不总是一种选择。【参考方案6】:

将您的可查询列表设置为 .ToList() ,它应该可以正常工作。

【讨论】:

请提供一个例子而不是仅仅发布一个解决方案。【参考方案7】:

仅供参考:从一本书中调整了一些行,因为它仍然有效:

调用 SaveChanges() 方法会启动一个事务,如果在迭代完成之前发生异常,该事务会自动回滚所有持久化到数据库的更改;否则事务提交。您可能想在每次实体更新或删除后应用该方法,而不是在迭代完成后应用该方法,尤其是当您更新或删除大量实体时。

如果您在处理所有数据之前尝试调用 SaveChanges(),则会引发“不允许新事务,因为会话中正在运行其他线程”异常。发生异常是因为 SQL Server 不允许在打开了 SqlDataReader 的连接上启动新事务,即使连接字符串启用了多个活动记录集 (MARS)(EF 的默认连接字符串启用 MARS)

有时最好理解事情发生的原因;-)

【讨论】:

避免这种情况的一个好方法是,当您打开一个阅读器以打开第二个阅读器并将这些操作放入第二个阅读器时。这是您在实体框架中更新主/详细信息时可能需要的东西。您为主记录打开第一个连接,为详细记录打开第二个连接。如果您只是阅读,应该没有问题。更新过程中出现的问题。 有用的解释。你说得对,很高兴了解事情发生的原因。 在多个 Stack Overflow 问题中,这只是 20 个正确答案。 但是这种情况发生在我身上的唯一一次是它确实涉及多个线程。一个线程正在读取连接,而另一个线程尝试在同一连接上执行 SaveChanges(全部通过实体框架,但我认为这并不重要)。 是的,我也是这样。我每晚都在数据库中读取完整的 LDAP 目录。所以因为要导入的数据很多,所以我使用 254 线程来处理这个。.NET 很快,但数据库事务不是,所以你可以帮助这个进程使用 254 线程。如今,这是一种快速导入大量数据的标准处理方式。如果导入很简单,则可以通过其他方式完成,但在复杂的数据转换中,这是完美的。【参考方案8】:

始终将您的选择用作列表

例如:

var tempGroupOfFiles = Entities.Submited_Files.Where(r => r.FileStatusID == 10 && r.EventID == EventId).ToList();

然后在保存更改的同时循环遍历集合

 foreach (var item in tempGroupOfFiles)
             
                 var itemToUpdate = item;
                 if (itemToUpdate != null)
                 
                     itemToUpdate.FileStatusID = 8;
                     itemToUpdate.LastModifiedDate = DateTime.Now;
                 
                 Entities.SaveChanges();

             

【讨论】:

这根本不是一个好习惯。如果您不需要,您不应该经常执行 SaveChanges,而且您绝对不应该“始终将您的选择用作列表” @Dinerdo 这真的取决于场景。就我而言,我有 2 个 foreach 循环。外层将 db 查询作为列表。例如,这个 foreach 遍历硬件设备。内部 foreach 从每个设备检索多个数据。根据要求,我需要在从每个设备中一一检索数据后将其保存到数据库中。在过程结束时保存所有数据不是一个选项。我遇到了同样的错误,但 mzonerz 的解决方案有效。 @jstuardo 即使使用批处理? @Dinerdo 我同意这不是哲学层面的好习惯。但是,在 for 循环中存在几种情况,代码调用另一个方法(比如说 AddToLog() 方法),其中包括对本地 db.SaveChanges() 的调用。在这种情况下,您无法真正控制对 db.Save Changes 的调用。在这种情况下,使用 ToList() 或类似的结构将按照 mzonerz 的建议工作。谢谢! 实际上,这对您的伤害大于帮助。我坚持我所说的 - ToList() 绝对不应该一直使用,并且在每个项目之后保存更改是在高性能应用程序中尽可能避免的事情。这将是一个临时修复 IMO。无论您使用哪种日志记录方法,理想情况下都应该利用缓冲。【参考方案9】:

我遇到了同样的问题,但情况不同。我在列表框中有一个项目列表。用户可以单击一个项目并选择删除,但我使用存储过程来删除该项目,因为删除该项目涉及很多逻辑。当我调用存储过程时,删除工作正常,但以后对 SaveChanges 的任何调用都会导致错误。我的解决方案是在 EF 之外调用存储的过程,这很好。出于某种原因,当我使用 EF 做事方式调用存储过程时,它会留下一些东西。

【讨论】:

最近有类似的问题:在我的例子中,原因是存储过程中的SELECT 语句产生了空结果集,如果没有读取该结果集,SaveChanges 会抛出该异常。 与 SP 的未读结果相同,非常感谢提示)【参考方案10】:

这里还有另外 2 个选项,可让您在每个循环中调用 SaveChanges()。

第一个选项是使用一个 DBContext 来生成要迭代的列表对象,然后创建第二个 DBContext 来调用 SaveChanges()。这是一个例子:

//Get your IQueryable list of objects from your main DBContext(db)    
IQueryable<Object> objects = db.Object.Where(whatever where clause you desire);

//Create a new DBContext outside of the foreach loop    
using (DBContext dbMod = new DBContext())
   
    //Loop through the IQueryable       
    foreach (Object object in objects)
    
        //Get the same object you are operating on in the foreach loop from the new DBContext(dbMod) using the objects id           
        Object objectMod = dbMod.Object.Find(object.id);

        //Make whatever changes you need on objectMod
        objectMod.RightNow = DateTime.Now;

        //Invoke SaveChanges() on the dbMod context         
        dbMod.SaveChanges()
    

第二个选项是从 DBContext 中获取数据库对象列表,但只选择 id。然后遍历 id 的列表(可能是一个 int)并获取与每个 int 对应的对象,并以这种方式调用 SaveChanges()。这种方法背后的想法是获取大量整数列表,比获取大量 db 对象列表并在整个对象上调用 .ToList() 效率更高。下面是这个方法的一个例子:

//Get the list of objects you want from your DBContext, and select just the Id's and create a list
List<int> Ids = db.Object.Where(enter where clause here)Select(m => m.Id).ToList();

var objects = Ids.Select(id => db.Objects.Find(id));

foreach (var object in objects)

    object.RightNow = DateTime.Now;
    db.SaveChanges()

【讨论】:

这是我想到并做到的一个很好的选择,但这需要被赞成。注意:i)您可以迭代为可枚举的,这对于非常大的集合很有用; ii) 您可以使用 NoTracking 命令来避免加载这么多记录时出现问题(如果这是您的情况); iii)我也非常喜欢仅主键选项 - 这非常聪明,因为您将更少的数据加载到内存中,但您不会在潜在的动态基础数据集上处理 Take/Skip。【参考方案11】:

如果由于 foreach 而出现此错误,并且您确实需要首先在循环中保存一个实体并在循环中进一步使用生成的标识,就像我的情况一样,最简单的解决方案是使用另一个 DBContext 插入将返回的实体Id 并在外部上下文中使用此 Id

例如

    using (var context = new DatabaseContext())
    
        ...
        using (var context1 = new DatabaseContext())
        
            ...
               context1.SaveChanges();
                                 
        //get id of inserted object from context1 and use is.   
      context.SaveChanges();
   

【讨论】:

【参考方案12】:

从 EF5 迁移到 EF6 后,我们开始看到此错误“不允许新事务,因为会话中还有其他线程正在运行”

Google 把我们带到了这里,但我们没有在循环内调用 SaveChanges()。使用从 DB 读取的 foreach 循环中的 ObjectContext.ExecuteFunction 执行存储过程时引发错误。

对 ObjectContext.ExecuteFunction 的任何调用都会将函数包装在事务中。在已经有打开的读取器的情况下开始事务会导致错误。

可以通过设置以下选项来禁用在事务中包装 SP。

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

EnsureTransactionsForFunctionsAndCommands 选项允许 SP 在不创建自己的事务的情况下运行,并且不再引发错误。

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands Property

【讨论】:

【参考方案13】:

所以在项目中我遇到了同样的问题,问题不在foreach.toList() 中,实际上是在我们使用的 AutoFac 配置中。 这造成了一些奇怪的情况,即引发了上述错误,但也引发了许多其他等效错误。

这是我们的修复: 改变了这个:

container.RegisterType<DataContext>().As<DbContext>().InstancePerLifetimeScope();
container.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();

收件人:

container.RegisterType<DataContext>().As<DbContext>().As<DbContext>();
container.RegisterType<DbFactory>().As<IDbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().As<IUnitOfWork>();//.InstancePerRequest();

【讨论】:

您能否详细说明您认为问题出在哪里?你通过每次创建一个新的 Dbcontext 解决了这个问题?【参考方案14】:

我知道这是一个老问题,但我今天遇到了这个错误。

我发现,当数据库表触发器出错时会引发此错误。

供您参考,当您收到此错误时,您也可以检查您的表触发器。

【讨论】:

【参考方案15】:

我需要读取一个巨大的 ResultSet 并更新表中的一些记录。 我尝试按照Drew Noakes 的answer 中的建议使用块。

不幸的是,在 50000 条记录之后,我遇到了 OutofMemoryException。 答案Entity framework large data set, out of memory exception 解释说,

EF 创建用于更改检测的第二个数据副本(因此 它可以持久化对数据库的更改)。 EF持有这第二盘 在上下文的整个生命周期中,它的这个集合让你筋疲力尽 内存。

建议为每个批次重新创建上下文。

所以我检索了主键的最小值和最大值——表的主键作为自动增量整数。然后我通过打开每个块的上下文从数据库中检索记录块。处理完块上下文后关闭并释放内存。它确保内存使用量不会增长。

下面是我的代码中的一个 sn-p:

  public void ProcessContextByChunks ()
  
        var tableName = "MyTable";
         var startTime = DateTime.Now;
        int i = 0;
         var minMaxIds = GetMinMaxIds();
        for (int fromKeyID= minMaxIds.From; fromKeyID <= minMaxIds.To; fromKeyID = fromKeyID+_chunkSize)
        
            try
            
                using (var context = InitContext())
                   
                    var chunk = GetMyTableQuery(context).Where(r => (r.KeyID >= fromKeyID) && (r.KeyID < fromKeyID+ _chunkSize));
                    try
                    
                        foreach (var row in chunk)
                        
                            foundCount = UpdateRowIfNeeded(++i, row);
                        
                        context.SaveChanges();
                    
                    catch (Exception exc)
                    
                        LogChunkException(i, exc);
                    
                
            
            catch (Exception exc)
            
                LogChunkException(i, exc);
            
        
        LogSummaryLine(tableName, i, foundCount, startTime);
    

    private FromToRange<int> GetminMaxIds()
    
        var minMaxIds = new FromToRange<int>();
        using (var context = InitContext())
        
            var allRows = GetMyTableQuery(context);
            minMaxIds.From = allRows.Min(n => (int?)n.KeyID ?? 0);  
            minMaxIds.To = allRows.Max(n => (int?)n.KeyID ?? 0);
        
        return minMaxIds;
    

    private IQueryable<MyTable> GetMyTableQuery(MyEFContext context)
    
        return context.MyTable;
    

    private  MyEFContext InitContext()
    
        var context = new MyEFContext();
        context.Database.Connection.ConnectionString = _connectionString;
        //context.Database.Log = SqlLog;
        return context;
    

FromToRange 是一个具有 From 和 To 属性的简单结构。

【讨论】:

我没看到你是如何“更新”你的上下文的。看起来您只是在为每个块创建一个新的上下文。 @Suncat2000,你是对的,上下文应该是一个短暂的对象***.com/questions/43474112/…【参考方案16】:

我也遇到了同样的问题。

这是原因和解决方法。

http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx

确保在触发插入、更新等数据操作命令之前,您已关闭所有以前活动的 SQL 阅读器。

最常见的错误是从 db 读取数据并返回值的函数。 例如,像 isRecordExist 这样的函数。

在这种情况下,如果我们找到记录并忘记关闭阅读器,我们会立即从函数中返回。

【讨论】:

实体框架中的“关闭阅读器”是什么意思?在类似 var result = from customer in myDb.Customers where customer.Id == customerId select customer; 这样的查询中没有可见的阅读器返回结果.FirstOrDefault(); @Anthony 正如其他答案所说,如果您使用 EF 枚举 LINQ 查询(IQueryable),则底层 DataReader 将保持打开状态,直到最后一行被迭代。但是,尽管 MARS 是在连接字符串中启用的一项重要功能,但仅靠 MARS 仍然无法解决 OP 中的问题。问题是在底层 DataReader 仍处于打开状态时尝试 SaveChanges。【参考方案17】:

下面的代码对我有用:

private pricecheckEntities _context = new pricecheckEntities();

...

private void resetpcheckedtoFalse()

    try
    
        foreach (var product in _context.products)
        
            product.pchecked = false;
            _context.products.Attach(product);
            _context.Entry(product).State = EntityState.Modified;
        
        _context.SaveChanges();
    
    catch (Exception extofException)
    
        MessageBox.Show(extofException.ToString());

    
    productsDataGrid.Items.Refresh();

【讨论】:

欢迎来到 SO!考虑添加说明为什么对您有用的解释和/或链接。对于 SO,只有代码的答案通常被认为质量不好。【参考方案18】:

在我的例子中,当我通过 EF 调用存储过程,然后 SaveChanges 抛出这个异常时,问题就出现了。问题是在调用程序时,枚举数没有被处理。我通过以下方式修复了代码:

public bool IsUserInRole(string username, string roleName, DataContext context)
          
   var result = context.aspnet_UsersInRoles_IsUserInRoleEF("/", username, roleName);

   //using here solved the issue
   using (var en = result.GetEnumerator()) 
   
     if (!en.MoveNext())
       throw new Exception("emty result of aspnet_UsersInRoles_IsUserInRoleEF");
     int? resultData = en.Current;

     return resultData == 1;//1 = success, see T-SQL for return codes
   

【讨论】:

【参考方案19】:

最近我在我的项目中遇到了同样的问题,所以发布我的经验,它可能会帮助一些和我在同一条船上的人。问题是由于我正在遍历 EF 选择查询的结果(结果未检索到内存中)。

var products = (from e in _context.Products
                              where e.StatusId == 1
                              select new  e.Name, e.Type );

        foreach (var product in products)
        
           //doing some insert EF Queries
           //some EF select quries
            await _context.SaveChangesAsync(stoppingToken); // This code breaks.
        

我已经更新了我的 Products 选择查询以将结果带入 LIST 而不是 IQueryable(这似乎在每个循环中都打开了阅读器,因此保存失败)。

 var products = (from e in _context.Products
                              where e.StatusId == 1
                              select new  e.Name, e.Type )**.ToList()**; //see highlighted

【讨论】:

【参考方案20】:

我迟到了,但今天我遇到了同样的错误,我的解决方法很简单。我的场景类似于我在嵌套的 for-each 循环中进行数据库事务的给定代码。

问题是单个 DB 事务比 for-each 循环花费的时间要长一点,所以一旦前面的事务没有完成,那么新的牵引力就会抛出异常,所以解决方案是在 for 中创建一个新对象-您进行数据库事务的每个循环。

对于上述场景,解决方案如下:

foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
                    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 ### //
                    
                

【讨论】:

【参考方案21】:

我有点晚了,但我也有这个错误。我通过检查更新的值在哪里解决了这个问题。

我发现我的查询是错误的,并且有超过 250 多个编辑待处理。所以我更正了我的查询,现在它可以正常工作了。

所以在我的情况下:通过调试查询返回的结果来检查查询是否有错误。之后更正查询。

希望这有助于解决未来的问题。

【讨论】:

以上是关于来自实体框架的 SqlException - 不允许新事务,因为会话中正在运行其他线程的主要内容,如果未能解决你的问题,请参考以下文章

实体框架(Web API)中的 System.Data.SqlClient.SqlException

未提供的参数化查询。实体框架

实体框架核心:无效的列名“UserId1”

来自 sql 查询执行实体框架的匿名类型结果

如何使用实体框架关联来自多个上下文的对象

C# 实体框架向来自外部库的实体添加新列