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

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.

其他回答

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

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

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

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

在启用MARS和将整个结果集检索到内存之间有一个很好的中间地带,那就是在初始查询中只检索id,然后循环遍历实现每个实体的id。

例如(使用这个答案中的“博客和帖子”样本实体):

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也需要这样做(参见这里和这里)。

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

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

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.

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

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

这对我很有用……无论你的连接是在app.config中还是通过编程方式设置的… 希望这对大家有帮助