entity framework:已经有一个开放的DataReader与这个Command相关联

我正在使用entity framework,偶尔我会得到这个错误。

EntityCommandExecutionException {"There is already an open DataReader associated with this Command which must be closed first."} at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands... 

即使我没有做任何手动连接pipe理。

这个错误间歇发生。

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

  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已经打开,为什么不重新使用连接。

这不是closures连接。 EF正确地pipe理连接。 我对这个问题的理解是,在单个连接(或多个select的单个命令)上执行多个数据检索命令,而下一个DataReader在第一个完成读取之前执行。 避免exception的唯一方法是允许多个嵌套的DataReaders =打开MultipleActiveResultSets。 当总是发生这种情况时,另一个场景是迭代查询结果(IQueryable),并且您将在迭代中触发加载实体的延迟加载。

或者使用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()像这样做一个简单的解决scheme:

 var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList(); 

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

我意识到,如果你想延迟一些属性例如这可能不被期望。 这主要是一个例子,希望解释如何/为什么你会得到这个问题,所以你可以做出相应的决定

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

这个问题是由懒加载导致的,所以避免这种情况的一种方法是不使用延迟加载,通过使用Include:

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

如果您使用适当的Include ,您可以避免启用MARS。 但是如果你错过了一个,你会得到这个错误,所以启用MARS可能是解决这个问题的最简单的方法。

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

 foreach (var user in _dbContext.Users) { } 

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

 _dbContext.Users.ToList() 

注意:.ToList()每次都会创build一个新的集合,如果您正在处理大量数据,则会导致性能问题。

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

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

尝试在你的连接string设置“MultipleActiveResultSets = true”这允许在数据库上的多任务处理。 “Server = yourserver; AttachDbFilename =数据库;用户ID = SA;密码=等等; MultipleActiveResultSets =真;应用程序= EntityFramework”多数民众赞成在我工作…无论你的连接在app.config或你设置它编程…希望这有帮助

我最初决定在我的API类中使用静态字段来引用MyDataContext对象(其中MyDataContext是EF5上下文对象)的实例,但这似乎是造成这个问题的原因。 我给每个API方法添加了类似下面的代码,并解决了这个问题。

 using(MyDBContext db = new MyDBContext()) { //Do some linq queries } 

正如其他人所说,EF数据上下文对象不是线程安全的。 所以把它们放在静态对象中最终会在正确的条件下导致“数据读取器”错误。

我原来的假设是只创build一个对象的实例会更有效率,并提供更好的内存pipe理。 从我所研究的这个问题来看,事实并非如此。 事实上,将每个对API的调用视为一个独立的线程安全事件似乎更有效率。 确保所有资源得到妥善释放,因为对象超出范围。

这很有意义,特别是如果你把你的API到下一个自然的进展,将它作为一个WebService或REST API公开。

泄露

  • 操作系统:Windows Server 2012
  • .NET:安装4.5,项目使用4.0
  • 数据来源:MySQL
  • 应用程序框架:MVC3
  • 身份validation:表单

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

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

简单的解决scheme是在使用集合之前使用.ToList() 。 另请注意,MARS不适用于MySQL。

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

例如(在这个答案中使用“博客和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(); } } 

这样做意味着您只需将数千个整数存入内存,而不是成千上万的整个对象图表,这样可以最大限度地减less内存使用量,同时使您能够在不启用MARS的情况下逐项工作。

从样本中可以看出,这样做的另一个好处是,您可以在循环遍历每个项目时保存更改,而不必等到循环结束(或其他某种解决方法),甚至可能需要MARS启用(见这里和这里 )。

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

一旦我把所有的Func's换成了Expression's ,exception停止被抛出。

我相信EntityFramworkExpression's来做一些聪明的事情,而Func'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) { .... } 

这会抛出exception,如果我们尝试在Where()中使用它,我们应该做的是build立一个像这样的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; } 

我在第二个查询之前使用下面的代码部分解决了这个问题:

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

你可以改变睡眠时间,以毫秒为单位

PD在使用线程时很有用