ChatGPT解决这个技术问题 Extra ChatGPT

来自实体框架的 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 - 该模型位于我们开发服务器上的数据库中。 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 中(如果它还没有的话)。第一次通过应该是一个完整的库存。每次它在那里运行时都不会发生任何事情,除非新的库存在一夜之间进入饲料。

所以最大的问题 - 我如何解决我得到的交易错误?我是否需要每次通过循环删除并重新创建我的上下文(对我来说没有意义)?

这是我见过的最详细的问题。
有人想念存储过程吗?

J
John Saunders

在大量拔出头发后,我发现 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() 真的很有用!
D
Drew Noakes

正如您已经确定的那样,您无法从仍在通过活动阅读器从数据库中绘制的 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<T>.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,显示了数据库创建的内容。

第一个循环不需要跳过任何内容,因此更简单。

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”是一个简单的解决方案:)
d
d219

我们现已发布对 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 - 例如,如果您将交互式调试代码制作数据库调用。这是将事务超时延长到一小时的代码: using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0)))
我第一次从“教程路径”脱离到我自己的真实示例时就遇到了这个错误!然而,对我来说,更简单的解决方案,迭代后保存,更好! (我认为 99% 的情况是这样,只有 1% 的人真的必须在循环内执行数据库保存)
总的。我刚刚遇到了这个错误。非常讨厌。第二个建议对我来说就像一个魅力,同时将我的 SaveChanges 移动到循环中。我认为在循环之外保存更改更适合批量更改。不过没关系。我猜不是?! :(
.NET 4.5 对我不起作用。使用 TransactionScope 时,我收到以下错误“EnlistTransaction 上的基础提供程序失败。{”合作伙伴事务管理器已禁用其对远程/网络事务的支持。 (来自 HRESULT 的异常:0x8004D025)“}”。我最终在迭代之外完成了这项工作。
使用 TransactionScope 是危险的,因为表在整个事务期间被锁定。
P
Paul Roub

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

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

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

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


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

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


这是我在我的案例中发现的更好的选择,因为它保存在 foreach 中
这并不总是一种选择。
W
Wojciech Seweryn

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


请提供示例,而不仅仅是发布解决方案。
d
d219

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

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

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

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


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

始终将您的选择用作列表

例如:

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。无论您使用哪种记录方法,理想情况下都应该利用缓冲。
M
MikeKulls

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


最近有类似的问题:在我的情况下,原因是存储过程中的 SELECT 语句产生了空结果集,如果未读取该结果集,SaveChanges 会抛出该异常。
与SP的未读结果相同,非常感谢提示)
j
jjspierx

这里还有另外 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。
H
Hemant Sakta

如果由于 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();
   }

J
JamPickle

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

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

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

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

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

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

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands Property


V
Vinod T. Patil

我也面临同样的问题。

这是原因和解决方法。

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。
V
VeldMuijz

因此,在项目中,如果我遇到了同样的问题,问题不在 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 来解决这个问题?
n
nadir

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

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

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


M
Michael Freidgeim

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

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

EF 创建用于更改检测的第二个数据副本(以便它可以将更改持久保存到数据库)。 EF 在上下文的整个生命周期中都保留了第二组,而它的这组会使您的内存不足。

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

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

以下是我的代码片段:

  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,你是对的,上下文应该是一个短暂的对象stackoverflow.com/questions/43474112/…
u
user1551655

最近我在我的项目中遇到了同样的问题,所以发布我的经验,它可能会帮助一些和我在同一条船上的人。问题是由于我正在循环浏览 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

u
ugo

下面的代码对我有用:

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,仅代码的答案通常被认为质量不好。
T
Tomas Kubes

就我而言,当我通过 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
   }
}

U
Usman

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

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

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

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 ### //
                    }
                }

M
Max

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

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

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

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


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅