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

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


当前回答

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

其他回答

这个问题可以简单地通过将数据转换为列表来解决

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

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

foreach (var user in _dbContext.Users)
{    
}

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

_dbContext.Users.ToList()

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

我最初决定在我的API类中使用一个静态字段来引用MyDataContext对象的实例(其中MyDataContext是一个EF5 Context对象),但这似乎是造成问题的原因。我在每个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 身份验证:形式

或者使用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循环中遍历它时,它不再使用数据读取器来打开列表,而是在内存中。

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

在我的情况下,问题发生是因为依赖注入注册。我正在将每个请求范围的服务注入到一个单例注册服务中,该服务使用dbcontext。因此在多个请求中使用了dbcontext,因此出现了错误。