我正在使用实体框架,偶尔我会得到这个错误。

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不重用一个连接,如果它已经打开。


当前回答

It is not about closing connection. EF manages connection correctly. My understanding of this problem is that there are multiple data retrieval commands executed on single connection (or single command with multiple selects) while next DataReader is executed before first one has completed the reading. The only way to avoid the exception is to allow multiple nested DataReaders = turn on MultipleActiveResultSets. Another scenario when this always happens is when you iterate through result of the query (IQueryable) and you will trigger lazy loading for loaded entity inside the iteration.

其他回答

如果我们试图将部分条件分组到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

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

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

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

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

或者使用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();

这迫使entityframework将列表加载到内存中,因此当我们在foreach循环中遍历它时,它不再使用数据读取器来打开列表,而是在内存中。

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

我发现我有同样的错误,它发生在我使用Func<TEntity时,bool>而不是表达式<Func<TEntity, bool>>作为谓词。

一旦我改变了所有的Func的表达式的异常停止抛出。

我相信entityframework在Expression中做了一些聪明的事情,这是它在Func中做不到的

在我的情况下,我发现在myContext.SaveChangesAsync()调用之前缺少“await”语句。在这些异步调用之前添加await,为我解决了数据读取器的问题。