Entity Framework中的SqlException-不允许新事务,因为会话中正在运行其他线程


600

我目前收到此错误:

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 Server上的数据库中,并且每天通过自动Feed进行更新。 替代文字http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png

注意-模型#1中带红色圆圈的项目是我用来“映射”到模型#2的字段。请忽略模型2中的红色圆圈:这是我遇到的另一个问题,现在可以回答。

注意:我仍然需要放入isDeleted支票,以便在DB1超出我们客户的库存范围时可以将其从DB1中软删除。

我要做的就是用这个特定的代码将DB1中的一家公司与DB2中的一个客户连接起来,从DB2中获取他们的产品列表,如果还没有,请在DB1中插入它。第一次应该是充分利用库存。每次在此运行时,除非夜间通入饲料中的新库存,否则什么都不会发生。

因此,最大的问题-如何解决我遇到的交易错误?每次循环时都需要删除并重新创建上下文(对我来说没有意义)吗?


6
这是我见过的最详细的问题。

9
有人错过存储过程了吗?
David

Answers:


690

拔掉头发后,我发现发foreachloop是罪魁祸首。需要发生的是调用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;
    // ...
}

14
是的,这也让我头疼。发现问题时,我差点从椅子上摔下来!我了解问题背后的技术原因,但这不是直观的,也无助于开发人员陷入“成功之坑” blogs.msdn.com/brada/archive/2003/10/02/50420。 aspx
医生琼斯医生2010年

9
大型数据集的性能不是很糟糕吗?如果表中有一百万条记录。ToList()会将它们全部吸入内存。我遇到了这个问题,想知道以下是否可行a)分离实体b)创建一个新的ObjectContext并将分离的实体附加到它。c)在新的ObjectContext上调用SaveChanges()d)从新的ObjectContext分离实体e)将其返回到旧的ObjectContext
Abhijeet Patel 2010年

149
问题是您SaveChanges仍在从数据库中提取结果时无法调用。因此,另一种解决方案是在循环完成后保存更改。
德鲁·诺阿克斯

4
也被咬过,我将其添加到Microsoft Connect:connect.microsoft.com/VisualStudio/feedback/details/612369/… 随时对其进行投票。
伊恩·默瑟

36
我们的开发人员倾向于将.ToList()附加到任何LINQ查询中,而无需考虑后果。这必须是第一次附加.ToList()真的很有用!
马克

266

如您所知,您无法从 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事件探查器后重新讨论了这个问题,并更新了一些东西来提高性能。对于感兴趣的任何人,这里有一些示例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

17
谢谢。您的解释比标记为“已回答”的解释有用得多。
瓦格纳·席尔瓦

1
这很棒。只是一件事:如果要查询一列并更新该列的值,则需要注意chunkNumber ++;。假设您有一列“ ModifiedDate”,并且要查询.Where(x => x.ModifiedDate!= null),然后在foreach的最后为ModifiedDate设置一个值。这样,您不会迭代一半的记录,因为一半的记录已被跳过。
2015年

不幸的是,在庞大的数据集上,您将获得OutofMemoryException-请参阅Entity Framework大数据集的解释 ,内存不足异常。我已经描述了如何在每一批中更新您的上下文从Entity Framework的SqlException中 -不允许新事务,因为会话中有其他线程在运行
Michael Freidgeim,2016年

我认为这应该有效。var skip = 0; const int take = 100; List <Employee> emps; while((emps = db.Employees.Skip(skip).Take(take).ToList())。Count> 0){跳过+ =获取; foreach(以emps为单位的emp emp){//在这里做些事情}}我会为这个问题制定一个答案,但是它会埋在下面的一堆答案中,并且与这个问题有关。
jwize

123

现在,我们已经对Connect上打开的错误发布了官方回复。我们建议的解决方法如下:

此错误是由于Entity Framework在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();
    }
} 

6
如果您采用事务处理路由,则仅抛出TransactionScope可能无法解决该问题-如果您正在做的事情可能花费很长时间,请不要忘记延长超时时间-例如,如果您将交互式调试代码,数据库调用。以下代码将事务超时延长至一个小时:使用(var transaction = new TransactionScope(TransactionScopeOption.Required,new TimeSpan(
1,0,0

我第一次脱离“教程路径”进入我自己的真实示例时,就碰到了这个错误!但是,对我而言,更简单的解决方案,即“迭代后保存”会更好!(我认为99%的情况是这种情况,只有1%的人必须在循环内真正保存数据库)
Spiderman 2012年

毛。我只是碰到这个错误。真讨厌 将我的SaveChanges移到循环中后,第二个建议对我来说就像一个魅力。我认为将更改保存在循环外比较适合批量更改。但是,好吧。我猜不会?!:(
杨先生

.NET 4.5不适用于我。使用TransactionScope时,出现以下错误“基础提供程序在EnlistTransaction上失败。{”伙伴事务管理器已禁用其对远程/网络事务的支持。(来自HRESULT的异常:0x8004D025)“}”。我最终在迭代之外完成了这项工作。
Diganta Kumar

使用TransactionScope是危险的,因为表在整个事务期间都处于锁定状态。
Michael Freidgeim

19

实际上,您无法foreach使用Entity Framework 将更改保存在C#的循环内。

context.SaveChanges() 方法的行为就像在常规数据库系统(RDMS)上的提交一样。

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

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


2
我认为在这里看到“常规数据库系统(RDMS)”很有趣
Dinerdo

1
这似乎是错误的,因为在EF中90%的上下文中重复调用SaveChanges是可以的。
Pxtl

似乎反复调用SaveChanges很好,除非foreach循环在db Entity上迭代。
kerbasaurus

1
啊哈!将上下文带入for-each循环!(pffft ...我在想什么?..)谢谢!
亚当·考克斯

18

只需放在(循环)context.SaveChanges()末尾foreach


这是我在我的情况下发现的更好的选择,这是因为保存在foreach中
Almeida

2
这并不总是一种选择。
Pxtl

9

一律使用您的选择作为列表

例如:

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();

             }

1
这根本不是一个好习惯。如果不需要,您不应该经常执行SaveChanges,并且绝对不应该“总是使用您的选择作为列表”
Dinerdo

@Dinerdo确实取决于场景。就我而言,我有2个foreach循环。外层的数据库查询作为列表。例如,此foreach遍历硬件设备。内部foreach从每个设备检索几个数据。根据要求,我需要将数据从每个设备一个接一个地保存到数据库中。在过程结束时,不能选择保存所有数据。我遇到了相同的错误,但mzonerz的解决方案有效。
jstuardo

@jstuardo即使有批处理?
Dinerdo

@Dinerdo我同意这不是哲学上的好习惯。但是,存在几种情况,其中在for循环内,代码调用了另一个方法(例如,AddToLog()方法),该方法包括本地对db.SaveChanges()的调用。在这种情况下,您无法真正控制对db.Save Changes的调用。在这种情况下,使用mListerz建议使用ToList()或类似的结构。谢谢!
A. Varma

实际上,这对您的伤害大于对您的帮助。我坚持我所说的-绝对不应该一直使用ToList(),并且在高性能应用程序中要尽可能避免在每一项之后保存更改。这将是IMO的临时修复程序。无论采用哪种日志记录方法,理想情况下还应该利用缓冲。
Dinerdo

8

仅供参考:从一本书和一些经调整的行开始,因为其有效期仍然有效:

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

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

有时最好了解为什么事情会发生;-)


1
避免这种情况的一种好方法是,当您打开一个阅读器以打开第二个阅读器并将这些操作放在第二个阅读器中时。当您在实体框架中更新主/明细时,这是您需要的。您为主记录打开第一个连接,为明细记录打开第二个连接。如果您只阅读,应该没有问题。在更新过程中出现问题。
Herman Van Der Blom 2014年

有用的解释。您说对了,最好了解事情为什么发生。
Dov Miller'3


5

我遇到了同样的问题,但情况有所不同。我在列表框中有一个项目列表。用户可以单击一个项目并选择Delete(删除),但是我正在使用存储的proc来删除该项目,因为删除该项目涉及很多逻辑。当我调用存储的proc时,删除工作正常,但是以后再调用SaveChanges都会导致错误。我的解决方案是在EF外部调用存储的proc,这很好用。出于某种原因,当我使用EF的处理方式调用存储的proc时,会留下一些空白。


3
最近有类似的问题:在我的情况下,原因是SELECT存储过程中的语句产生了空结果集,如果未读取该结果集,则会SaveChanges引发该异常。
2014年

SP的未读结果相同,非常感谢提示)
Pavel K

4

这是另外两个选项,允许您在每个循环中调用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。
托德

4

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

2

因此,在该项目是我有此相同的问题,问题并不在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解决了这个问题?
eran otzap,2016年

2

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

而且我发现,当数据库表触发器获取错误时,可以引发此错误。

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


2

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

不幸的是,在50000条记录之后,我遇到了OutofMemoryException。答案实体框架大数据集,内存不足异常说明,

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

@ Suncat2000,你是对的,背景应该是一个短版活的对象stackoverflow.com/questions/43474112/...
迈克尔Freidgeim

1

我也面临着同样的问题。

这是原因和解决方案。

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

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

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

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


7
“关闭读者”在实体框架中是什么意思?查询中没有可见的阅读器,例如myDb.Customers中的customer的var result =,其中customer.Id == customerId选择customer; 返回result.FirstOrDefault();
安东尼

@Anthony正如其他答案所说,如果您使用EF枚举LINQ查询(IQueryable),则底层DataReader将保持打开状态,直到对最后一行进行迭代为止。但是,尽管MARS是启用连接字符串的重要功能,但仅靠MARS仍无法解决OP中的问题。问题是在基础DataReader仍处于打开状态时尝试SaveChanges。
托德

1

以下代码对我有用:

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();
}

2
欢迎来到SO!考虑添加说明和/或链接,以说明为什么这对您有效。对于SO,通常仅将代码答案视为质量欠佳。
codeMagic 2014年

1

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

1

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

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

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

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

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

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

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands属性


0

我参加聚会很晚,但是今天我遇到了同样的错误,解决方法很简单。我的场景与给定代码相似,我在嵌套的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 ### //
                    }
                }

0

我有点迟了,但是我也有这个错误。我通过检查更新值的位置解决了问题。

我发现我的查询是错误的,那里有超过250多个编辑未决。所以我更正了我的查询,现在它可以正常工作了。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.