我使用的是Entity Framework,有时我会收到此错误。
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重用它。
这与关闭连接无关。 EF正确管理连接。我对这个问题的理解是,在第一个完成读取之前执行下一个DataReader时,在单个连接上执行多个数据检索命令(或具有多个选择的单个命令)。避免该异常的唯一方法是允许多个嵌套的DataReaders =启用MultipleActiveResultSets。这种情况总是发生的另一种情况是,当您遍历查询结果(IQueryable)时,您将在迭代过程中触发已加载实体的延迟加载。
Func<TEntity, bool>
代替Expression<Func<TEntity, bool>>
作为predicate
时发生。一旦我将所有Func's
更改为Expression's
,就会停止抛出异常。
[我相信EntityFramwork
对Expression's
做一些聪明的事情,而对Func's
根本做不了
.ToList()
查询,因此您可以打开新的DataReader遍历它。.Include
(// [要在查询中加载的其他实体 /)称为“预先加载”,它允许您(实际上)包括执行查询过程中的关联对象(实体)DataReader的。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()中使用它,将会抛出异常,我们应该做的是建立这样的谓词:
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积。使用线程时有用
或者使用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循环中对其进行迭代时,它不再使用数据读取器打开列表,而是在内存中。
例如,如果您要延迟加载某些属性,可能不需要这样做。这主要是一个示例,希望可以解释为什么/为什么会出现此问题,因此您可以做出相应的决定]
还有另一种解决这个问题的方法。是否更好的方法取决于您的情况。
问题是由于延迟加载导致的,所以一种避免它的方法是通过使用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()每次都会创建一个新集合,如果要处理大数据,可能会导致性能问题。
我通过向构造函数添加选项轻松地(实用)解决了这个问题。因此,我仅在需要时使用它。
public class Something : DbContext
{
public Something(bool MultipleActiveResultSets = false)
{
this.Database
.Connection
.ConnectionString = Shared.ConnectionString /* your connection string */
+ (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : "");
}
...
尝试输入连接字符串以设置MultipleActiveResultSets=true
。这允许在数据库上执行多任务。
Server=yourserver ;AttachDbFilename=database;User Id=sa;Password=blah ;MultipleActiveResultSets=true;App=EntityFramework
这对我有用...无论您是在app.config中还是以编程方式设置了连接...希望对您有所帮助
我最初决定在我的API类中使用静态字段来引用MyDataContext对象的实例(其中MyDataContext是EF5 Context对象),但这似乎是造成问题的原因。我向我的每个API方法添加了类似于以下内容的代码,从而解决了该问题。
using(MyDBContext db = new MyDBContext())
{
//Do some linq queries
}
正如其他人所述,EF数据上下文对象不是线程安全的。因此,将它们放置在静态对象中最终将在正确的条件下导致“数据读取器”错误。
我最初的假设是,仅创建对象的一个实例将更加高效,并提供更好的内存管理。从我收集的研究此问题的资料来看,事实并非如此。实际上,将对API的每次调用视为独立的线程安全事件似乎更为有效。当对象超出范围时,请确保正确释放所有资源。
这特别有意义,如果您将您的API带入下一个自然发展阶段,那就是将其公开为WebService或REST API。
foreach (var parent in ViewBag.parents)
{
foreach (var child in parent.childs)
{
}
}
简单的解决方案是在使用集合之前在集合上使用.ToList()
。另请注意,MARS不适用于MySQL。
例如(使用this answer中的“博客和帖子”示例实体:]
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(请参阅here和here)。