【问题标题】:Entity Framework: There is already an open DataReader associated with this Command实体框架:已经有一个打开的 DataReader 与此命令关联
【发布时间】:2026-02-22 04:50:02
【问题描述】:

我正在使用实体框架,有时我会收到此错误。

EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...

即使我没有进行任何手动连接管理。

此错误间歇性发生。

触发错误的代码(为便于阅读而缩短):

        if (critera.FromDate > x) {
            t= _tEntitites.T.Where(predicate).ToList();
        }
        else {
            t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
        }

使用 Dispose 模式以每次打开新连接。

using (_tEntitites = new TEntities(GetEntityConnection())) {

    if (critera.FromDate > x) {
        t= _tEntitites.T.Where(predicate).ToList();
    }
    else {
        t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
    }

}

还是有问题

如果连接已经打开,为什么 EF 不重用它。

【问题讨论】:

  • 我意识到这个问题很古老,但我很想知道您的 predicatehistoricPredicate 变量是什么类型。我发现如果你将Func&lt;T, bool&gt; 传递给Where(),它会编译并且有时会工作(因为它在内存中执行“位置”)。您应该做的是将Expression&lt;Func&lt;T, bool&gt;&gt; 传递给Where()

标签: linq entity-framework sql-server-2008


【解决方案1】:

这不是关闭连接。 EF 正确管理连接。我对这个问题的理解是,在单个连接(或具有多个选择的单个命令)上执行了多个数据检索命令,而下一个 DataReader 在第一个完成读取之前执行。避免该异常的唯一方法是允许多个嵌套的 DataReaders = 打开 MultipleActiveResultSets。经常发生这种情况的另一种情况是,当您迭代查询结果 (IQueryable) 时,您将触发迭代内加载实体的延迟加载。

【讨论】:

  • 这是有道理的。但每个方法中只有一个选择。
  • @Sonic:这就是问题所在。也许执行了不止一个命令,但您看不到它。我不确定这是否可以在 Profiler 中进行跟踪(在执行第二个读取器之前可以抛出异常)。您还可以尝试将查询强制转换为 ObjectQuery 并调用 ToTraceString 以查看 SQL 命令。很难追踪。我总是打开 MARS。
  • @Sonic:我无意检查已执行和已完成的 SQL 命令。
  • 太好了,我的问题是第二种情况:'当您遍历查询结果 (IQueryable) 时,您将触发迭代内加载实体的延迟加载。'
  • 启用 MARS can 显然有不好的副作用:designlimbo.com/?p=235
【解决方案2】:

还有另一种方法可以解决这个问题。这是否更好取决于您的情况。

问题是由延迟加载引起的,因此避免它的一种方法是不延迟加载,通过使用 Include:

var results = myContext.Customers
    .Include(x => x.Orders)
    .Include(x => x.Addresses)
    .Include(x => x.PaymentMethods);

如果您使用适当的Includes,则可以避免启用 MARS。但是如果你错过了一个,你就会得到错误,因此启用 MARS 可能是修复它的最简单方法。

【讨论】:

  • 工作就像一个魅力。 .Include 是比启用 MARS 更好的解决方案,也比编写自己的 SQL 查询代码容易得多。
  • 如果有人遇到您只能编写 .Include("string") 而不是 lambda 的问题,您需要添加“使用 System.Data.Entity”,因为扩展方法位于那里.
【解决方案3】:

除了使用 MARS (MultipleActiveResultSets),您还可以编写代码,这样您就不会打开多个结果集。

您可以做的是将数据检索到内存中,这样您就不会打开阅读器。 这通常是由于在尝试打开另一个结果集时迭代结果集造成的。

示例代码:

public class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogID { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostID { get; set; }
    public virtual Blog Blog { get; set; }
    public string Text { get; set; }
}

假设您正在查找包含以下内容的数据库:

var context = new MyContext();

//here we have one resultset
var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5); 

foreach (var blog in largeBlogs) //we use the result set here
{
     //here we try to get another result set while we are still reading the above set.
    var postsWithImportantText = blog.Posts.Where(p=>p.Text.Contains("Important Text"));
}

我们可以通过像这样添加 .ToList() 来解决这个问题:

var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList();

这迫使实体框架将列表加载到内存中,因此当我们在 foreach 循环中迭代它时,它不再使用数据读取器打开列表,而是在内存中。

我意识到,如果您想延迟加载某些属性,这可能不是您所希望的。 这主要是一个示例,希望能解释您如何/为什么会遇到此问题,以便您可以做出相应的决定

【讨论】:

  • 这个解决方案对我有用。在查询之后和对结果执行任何其他操作之前添加 .ToList()。
  • 注意这一点并使用常识。如果您ToListing 一千个对象,它将增加大量内存。在这个特定示例中,您最好将内部查询与第一个查询结合起来,这样只会生成一个查询而不是两个。
  • @subkamran 我的观点正是如此,思考一些事情并选择适合情况的事情,而不仅仅是做事。这个例子只是我想解释的随机的东西:)
  • 当然,我只是想为喜欢复制/粘贴的人明确指出 :)
  • 别开枪,但这绝不是问题的解决方案。从什么时候开始“将数据拉入内存”成为 SQL 相关问题的解决方案?我喜欢与数据库聊天,所以我绝不愿意在内存中拉一些东西“因为否则会引发 SQL 异常”。尽管如此,在您提供的代码中,没有理由两次联系数据库。一个电话轻松搞定。小心这样的帖子。 ToList, First, Single, ... 应该只在内存中需要数据时使用(所以只有你想要的数据),而不是在发生 SQL 异常时使用。
【解决方案4】:

我通过向构造函数添加选项轻松(实用)解决了这个问题。因此,我只在需要时使用它。

public class Something : DbContext
{
    public Something(bool MultipleActiveResultSets = false)
    {
        this.Database
            .Connection
            .ConnectionString = Shared.ConnectionString /* your connection string */
                              + (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : "");
    }
...

【讨论】:

  • 谢谢。它正在工作。我只是在 web.config 中直接在连接字符串中添加了 MultipleActiveResultSets=true
【解决方案5】:

当您尝试迭代的集合是一种延迟加载 (IQueriable) 时,您会收到此错误。

foreach (var user in _dbContext.Users)
{    
}

将 IQueriable 集合转换为其他可枚举集合将解决此问题。 例子

_dbContext.Users.ToList()

注意:.ToList() 每次都会创建一个新集合,如果您正在处理大数据,它可能会导致性能问题。

【讨论】:

  • 最简单的解决方案!大起来 ;)
  • 获取无限列表可能会导致严重的性能问题!怎么会有人对此表示赞同?
  • @SandRock 不适合为小公司工作的人 - SELECT COUNT(*) FROM Users = 5
  • 三思而后行。阅读此 Q/A 的年轻开发人员可能会认为这是一个历久弥新的解决方案,但事实并非如此。我建议您编辑您的答案,以警告读者从 db 获取无限列表的危险。
  • @SandRock 我认为这将是您链接描述最佳实践的答案或文章的好地方。
【解决方案6】:

我在第二次查询之前使用以下代码部分解决了这个问题:

 ...first query
 while (_dbContext.Connection.State != System.Data.ConnectionState.Closed)
 {
     System.Threading.Thread.Sleep(500);
 }
 ...second query

您可以以毫秒为单位更改睡眠时间

P.D.使用线程时很有用

【讨论】:

  • 在任何解决方案中任意添加 Thread.Sleep 都是不好的做法 - 当用于回避某个值的状态未完全理解的不同问题时尤其糟糕。我原以为响应底部所述的“使用线程”意味着至少对线程有一些基本的了解-但是此响应没有考虑任何上下文,尤其是在那些情况下这是一个非常糟糕的主意使用 Thread.Sleep - 例如在 UI 线程上。
【解决方案7】:

我最初决定在我的 API 类中使用静态字段来引用 MyDataContext 对象的实例(其中 MyDataContext 是 EF5 上下文对象),但这似乎是造成问题的原因。我在我的每个 API 方法中添加了类似以下的代码,从而解决了问题。

using(MyDBContext db = new MyDBContext())
{
    //Do some linq queries
}

正如其他人所说,EF 数据上下文对象不是线程安全的。因此将它们放在静态对象中最终会在适当的条件下导致“数据读取器”错误。

我最初的假设是只创建对象的一个​​实例会更有效,并且可以提供更好的内存管理。从我收集到的研究这个问题的资料来看,情况并非如此。事实上,将对 API 的每次调用视为一个独立的、线程安全的事件似乎更有效。确保在对象超出范围时正确释放所有资源。

这很有意义,特别是如果您将 API 带到下一个自然进程,即将其公开为 WebService 或 REST API。

披露

  • 操作系统:Windows Server 2012
  • .NET:安装 4.5,项目使用 4.0
  • 数据来源:MySQL
  • 应用程序框架:MVC3
  • 身份验证:表单

【讨论】:

    【解决方案8】:

    我注意到当我将 IQueriable 发送到视图并在双 foreach 中使用它时会发生此错误,其中内部 foreach 也需要使用连接。简单示例(ViewBag.parents 可以是 IQueriable 或 DbSet):

    foreach (var parent in ViewBag.parents)
    {
        foreach (var child in parent.childs)
        {
    
        }
    }
    

    简单的解决方案是在使用之前在集合上使用.ToList()。另请注意,MARS 不适用于 MySQL。

    【讨论】:

    • 谢谢!这里的所有内容都说“嵌套循环是问题”,但没有人说如何解决它。我在第一次调用时输入了ToList(),以从数据库中获取集合。然后我在该列表上做了一个foreach,随后的调用完美地工作而不是给出错误。
    • @AlbatrossCafe ...但是没有人提到在这种情况下您的数据将被加载到内存中,并且查询将在内存中执行,而不是在数据库中执行
    【解决方案9】:

    在启用 MARS 和将整个结果集检索到内存之间的一个很好的中间立场是,在初始查询中仅检索 ID,然后在执行过程中遍历实现每个实体的 ID。

    例如(使用 this answer 中的“博客和帖子”示例实体):

    using (var context = new BlogContext())
    {
        // Get the IDs of all the items to loop through. This is
        // materialized so that the data reader is closed by the
        // time we're looping through the list.
        var blogIds = context.Blogs.Select(blog => blog.Id).ToList();
    
        // This query represents all our items in their full glory,
        // but, items are only materialized one at a time as we
        // loop through them.
        var blogs =
            blogIds.Select(id => context.Blogs.First(blog => blog.Id == id));
    
        foreach (var blog in blogs)
        {
            this.DoSomethingWith(blog.Posts);
    
            context.SaveChanges();
        }
    }
    

    这样做意味着您只需将几千个整数拉入内存,而不是数千个整个对象图,这样可以最大限度地减少内存使用量,同时使您能够在不启用 MARS 的情况下逐项工作。

    如示例中所示,这样做的另一个好处是,您可以在遍历每个项目时保存更改,而不必等到循环结束(或其他类似的解决方法)即使启用了 MARS 也需要(请参阅 herehere)。

    【讨论】:

    • context.SaveChanges(); inside loop :( 。这不好。它必须在循环外。
    【解决方案10】:

    尝试在您的连接字符串中设置MultipleActiveResultSets=true。 这允许对数据库进行多任务处理。

    Server=yourserver ;AttachDbFilename=database;User Id=sa;Password=blah ;MultipleActiveResultSets=true;App=EntityFramework
    

    这对我有用...无论是您在 app.config 中的连接还是以编程方式设置它... 希望这有帮助

    【讨论】:

    • MultipleActiveResultSets=true 添加到您的连接字符串可能会解决问题。这不应该被否决。
    • 是的,我已经演示了如何添加到您的连接字符串
    • @AaronHudon 我相信,通常情况下,您想解释为什么事情会起作用,而不仅仅是陈述答案。
    【解决方案11】:

    我发现我有同样的错误,当我使用 Func&lt;TEntity, bool&gt; 而不是 Expression&lt;Func&lt;TEntity, bool&gt;&gt; 为您的 predicate 时发生了。

    一旦我将所有Func's 更改为Expression's,异常就会停止抛出。

    我相信EntityFramworkExpression's 做了一些聪明的事情,而Func's 根本没有这样做

    【讨论】:

    • 这需要更多的支持。我试图在我的 DataContext 类中制作一个方法,采用 (MyTParent model, Func&lt;MyTChildren, bool&gt; func),以便我的 ViewModels 可以为 Generic DataContext 方法指定某个 where 子句。在我这样做之前没有任何效果。
    【解决方案12】:

    如果我们尝试将部分条件分组到 Func 或扩展方法中,我们会得到这个错误,假设我们有这样的代码:

    public static Func<PriceList, bool> IsCurrent()
    {
      return p => (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) &&
                  (p.ValidTo == null || p.ValidTo >= DateTime.Now);
    }
    
    Or
    
    public static IEnumerable<PriceList> IsCurrent(this IEnumerable<PriceList> prices) { .... }
    

    如果我们尝试在 Where() 中使用它会抛出异常,我们应该做的是像这样构建一个 Predicate:

    public static Expression<Func<PriceList, bool>> IsCurrent()
    {
        return p => (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) &&
                    (p.ValidTo == null || p.ValidTo >= DateTime.Now);
    }
    

    更多内容可以阅读:http://www.albahari.com/nutshell/predicatebuilder.aspx

    【讨论】:

      【解决方案13】:

      这个问题只要把数据转换成列表就可以解决

       var details = _webcontext.products.ToList();
      
      
                  if (details != null)
                  {
                      Parallel.ForEach(details, x =>
                      {
                          Products obj = new Products();
                          obj.slno = x.slno;
                          obj.ProductName = x.ProductName;
                          obj.Price = Convert.ToInt32(x.Price);
                          li.Add(obj);
      
                      });
                      return li;
                  }
      

      【讨论】:

      • ToList() 进行调用,但上面的代码仍然没有处理连接。所以您的 _webcontext 在第 1 行时仍有被关闭的风险
      【解决方案14】:

      在我的情况下,由于依赖注入注册而出现问题。我将使用 dbcontext 的每个请求范围服务注入到单例注册服务中。因此,在多个请求中使用了 dbcontext,因此出现了错误。

      【讨论】:

        【解决方案15】:

        缓解此问题的两种解决方案:

        1. 使用.ToList() 强制内存缓存保持延迟加载 查询,因此您可以通过它打开一个新的 DataReader 进行迭代。
        2. .Include(/您要在查询中加载的其他实体/)这个 被称为急切加载,它允许您(确实)包括 在执行查询期间关联的对象(实体) 数据读取器。

        【讨论】:

          【解决方案16】:

          就我而言,我发现在 myContext.SaveChangesAsync() 调用之前缺少“等待”语句。在这些异步调用之前添加等待解决了我的数据读取器问题。

          【讨论】:

            【解决方案17】:

            在我的情况下,这个问题与 MARS 连接字符串无关,而是与 json 序列化有关。 将我的项目从 NetCore2 升级到 3 后,出现此错误。

            更多信息可以找到here

            【讨论】: