实体框架:已经有一个与此命令相关联的开放数据阅读器

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

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

199600 次浏览

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

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

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

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

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

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

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

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

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

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

foreach (var user in _dbContext.Users)
{
}
将可查询集合转换为其他可枚举集合将解决此问题。 示例< / p >
_dbContext.Users.ToList()

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

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

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

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

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

我最初决定在我的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
  • 身份验证:形式

我注意到,当我发送一个iquerible到视图并在双foreach中使用它时,这个错误发生了,其中内部foreach也需要使用连接。简单的例子(ViewBag。parent可以是iqueryable或者DbSet):

foreach (var parent in ViewBag.parents)
{
foreach (var child in parent.childs)
{


}
}

简单的解决方案是在使用集合之前对其使用.ToList()。还要注意,MARS不能与MySQL一起工作。

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

例如(使用“Blog”和“post”;样本实体如这个答案):

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

尝试在连接字符串中设置MultipleActiveResultSets=true

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

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

我发现我有同样的错误,它发生在我使用Func<TEntity, bool>而不是Expression<Func<TEntity, bool>>为你的predicate

一旦我将所有Func's更改为Expression's,异常就会停止抛出。

我相信EntityFramworkExpression's中做了一些聪明的事情,而在Func's中则没有

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

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

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

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

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

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

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

在我的情况下,这个问题与MARS连接字符串无关,而是与json序列化有关。 在将我的项目从NetCore2升级到3后,我得到了这个错误

更多信息可以找到在这里