It is not about closing connection. EF manages connection correctly. My understanding of this problem is that there are multiple data retrieval commands executed on single connection (or single command with multiple selects) while next DataReader is executed before first one has completed the reading. The only way to avoid the exception is to allow multiple nested DataReaders = turn on MultipleActiveResultSets. Another scenario when this always happens is when you iterate through result of the query (IQueryable) and you will trigger lazy loading for loaded entity inside the iteration.
Alternatively to using MARS (MultipleActiveResultSets) you can write your code so you dont open multiple result sets.
What you can do is to retrieve the data to memory, that way you will not have the reader open. It is often caused by iterating through a resultset while trying to open another result set.
Sample Code:
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; }
}
Lets say you are doing a lookup in your database containing these:
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"));
}
We can do a simple solution to this by adding .ToList() like this:
var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList();
This forces entityframework to load the list into memory, thus when we iterate though it in the foreach loop it is no longer using the data reader to open the list, it is instead in memory.
I realize that this might not be desired if you want to lazyload some properties for example. This is mostly an example that hopefully explains how/why you might get this problem, so you can make decisions accordingly
There's another way to overcome this problem. Whether it's a better way depends on your situation.
The problem results from lazy loading, so one way to avoid it is not to have lazy loading, through the use of Include:
var results = myContext.Customers
.Include(x => x.Orders)
.Include(x => x.Addresses)
.Include(x => x.PaymentMethods);
If you use the appropriate Include
s, you can avoid enabling MARS. But if you miss one, you'll get the error, so enabling MARS is probably the easiest way to fix it.
You get this error, when the collection you are trying to iterate is kind of lazy loading (IQueriable).
foreach (var user in _dbContext.Users)
{
}
Converting the IQueriable collection into other enumerable collection will solve this problem. example
_dbContext.Users.ToList()
Note: .ToList() creates a new set every-time and it can cause the performance issue if you are dealing with large data.
Try in your connection string to set MultipleActiveResultSets=true
.
This allow multitasking on database.
Server=yourserver ;AttachDbFilename=database;User Id=sa;Password=blah ;MultipleActiveResultSets=true;App=EntityFramework
That works for me ... whether your connection in app.config or you set it programmatically ... hope this helpful
I solved the problem easily (pragmatic) by adding the option to the constructor. Thus, i use that only when needed.
public class Something : DbContext
{
public Something(bool MultipleActiveResultSets = false)
{
this.Database
.Connection
.ConnectionString = Shared.ConnectionString /* your connection string */
+ (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : "");
}
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With