实体框架:已经有一个与此命令关联的开放DataReader

Son*_*oul 279 linq entity-framework sql-server-2008

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

EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...
Run Code Online (Sandbox Code Playgroud)

即使我没有做任何手动连接管理.

这个错误间歇性地发生.

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

仍有问题

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

Lad*_*nka 345

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

  • 好吧,我的问题是第二个场景:'当你遍历查询结果(IQueryable)时,你将在迭代中触发加载实体的延迟加载. (11认同)
  • 启用MARS _can_显然有不良副作用:http://www.designlimbo.com/?p = 235 (6认同)
  • 这是有道理的.但每种方法中只有一个选择. (2认同)
  • @Sonic:我的目的不是检查执行和完成的SQL命令. (2认同)

Jim*_*lff 123

或者使用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; }
}
Run Code Online (Sandbox Code Playgroud)

假设您在数据库中进行查找,包含以下内容:

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"));
}
Run Code Online (Sandbox Code Playgroud)

我们可以通过添加.ToList()来做一个简单的解决方案:

var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList();
Run Code Online (Sandbox Code Playgroud)

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

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

  • 小心这个并使用常识.如果你要"列出"一千个物体,它会增加记忆力.在这个具体的例子中,你最好将内部查询与第一个查询相结合,这样只生成一个查询而不是两个查询. (9认同)
  • 这个解决方案对我有用.在查询之后以及在对结果执行任何其他操作之前添加.ToList(). (7认同)
  • @subkamran我的观点正是如此,思考某事并选择适合于这种情况的东西,而不仅仅是做.这个例子只是随机我想的解释:) (4认同)
  • 当然,我只想明确指出复制/粘贴快乐的人:) (3认同)

Rya*_*ndy 67

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

这个问题是由延迟加载引起的,所以避免它的一种方法是不要通过使用Include来延迟加载:

var results = myContext.Customers
    .Include(x => x.Orders)
    .Include(x => x.Addresses)
    .Include(x => x.PaymentMethods);
Run Code Online (Sandbox Code Playgroud)

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

  • 如果有人遇到只能编写.Include("string")而不是lambda的问题,则需要添加"using System.Data.Entity",因为扩展方法位于那里. (14认同)

Nal*_*ran 46

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

foreach (var user in _dbContext.Users)
{    
}
Run Code Online (Sandbox Code Playgroud)

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

_dbContext.Users.ToList()
Run Code Online (Sandbox Code Playgroud)

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

  • 三思而后行.阅读此Q/A的年轻开发人员可能会认为这绝对不是一个全天候的解决方案.我建议你编辑你的答案,警告读者从db获取无限列表的危险. (5认同)
  • 最简单的解决方案!大UP;) (2认同)
  • 获取无界列表可能会导致严重的性能问题!怎么会有人点赞? (2认同)

小智 13

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

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

  • 谢谢.它正在发挥作用.我刚刚在web.config中的连接字符串中添加了MultipleActiveResultSets = true (2认同)

小智 8

尝试在您的连接字符串中设置"MultipleActiveResultSets = true",这允许在数据库上进行多任务处理."Server = yourserver; AttachDbFilename = database; User Id = sa; Password = blah; MultipleActiveResultSets = true; App = EntityFramework"这对我有用...无论你在app.config中的连接还是以编程方式设置它...希望这个有帮助