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

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


当前回答

你得到这个错误,当你试图迭代的集合是一种惰性加载(iquerible)。

foreach (var user in _dbContext.Users)
{    
}

将iquerible集合转换为其他可枚举集合将解决这个问题。 例子

_dbContext.Users.ToList()

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

其他回答

你得到这个错误,当你试图迭代的集合是一种惰性加载(iquerible)。

foreach (var user in _dbContext.Users)
{    
}

将iquerible集合转换为其他可枚举集合将解决这个问题。 例子

_dbContext.Users.ToList()

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

缓解这一问题的2个解决方案:

强制内存缓存保持延迟加载 查询,这样你就可以遍历它,打开一个新的DataReader。 .Include(/你想在查询中加载的附加实体/)this 叫做急装,它允许你(确实)包括 方法执行查询期间的关联对象(实体) DataReader。

在我的情况下,这个问题与MARS连接字符串无关,而是与json序列化有关。 在将我的项目从NetCore2升级到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();

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

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

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

这个问题是由惰性加载导致的,所以避免它的一种方法是不要有惰性加载,通过使用Include:

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

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