Async/Await Entity Framework ObjectContext disposed - c#

I'm running into this issue where I get the following error:
A first chance exception of type 'System.ObjectDisposedException' occurred in EntityFramework.dll
Additional information: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I get that error while trying to access a table using EF 6. I have set the method to async and await the return value, but it still gives me disposed error.
public async Task<List<Ticket>> GetAllOpenTwoTicketsAsync() {
using (AssetTrackingEntitiesObj _edmObj = new AssetTrackingEntitiesObj()) {
_edmObj.FullObjectContext.Connection.Open();
return await _edmObj.Tickets
.Where(t => t.TicketStatusType.isOpenTicket)
.Include(t => t.AssetTickets)
.Select(t => t).ToListAsync();
}
}
here is the method that calls the tickets
TicketsCollection = new ObservableCollection<Ticket>(await _ticketRepository.GetAllOpenTwoTicketsAsync());
Am I doing that correctly? Every method in my repository uses a using statement, creates its own objectcontext, opens its own connection and then does what ever it needs to, is this the correct manner for multiple async with EF6? Thanks in advance.

_edmObj.FullObjectContext.Connection.Open(); isn't needed. The using statement takes care of opening and disposing the context. That's the main reason to use them over opening/closing/disposing resources yourself.
.Select(t => t) is completely unnecessary. Just calling ToListAsync() will do the trick.
The rest of your code looks fine, so it's probably the first statement that is causing the error. Another cause could be that you try to access a navigation property that you didn't include, lazy loading doesn't work when your context is disposed.

Related

HttpContext.Current sometimes lost during async Entity Framework 6 query. Why the difference between the call stacks?

I'm debugging some code and noticed that occasionally, when it accesses the HttpContext.Current it is null and resorts to a fallback that was put in to handle that. The code is an async Web API method that calls down through the application layers and ultimately executes an async Entity Framework 6 query. (Code below) None of the layers in between do anything other than await [method call] - no .ConfigureAwait(false) or anything else.
The project has a System.Data.Entity.Infrastructure.IDbConnectionInterceptor which sets up the SQL session (for SQL RLS) in the Opened method. It uses an injected dependency which, in this case, gets the an ID it needs from the HttpContext.Current.Items collection. When I'm debugging, 95% of the time it works every time, but once in a while I found that HttpContext.Current and SynchronizationContext.Current are both null.
Looking at the Call Stack window, I can see they are arriving at the IDbConnectionInterceptor.Opened method in different ways. The successful version leads back down to my calling code in the Web API controller, but the version where it is null, leads back down to native code. I thought well maybe when it's not null, it's not even executing on a different thread, but Open does execute on a different thread from the original in both cases. My project is targeting .NET Framework 4.8 and referencing the Microsoft.AspNet.WebApi v5.2.3 nuget package. It has <httpRuntime targetFramework="4.7.2" /> under <system.web> in the config file (which I'm just now noticing does not match the 4.8 of the framework). My understanding is that as of .NET Framework 4.5, the context should flow across async calls so it seems like something is preventing that, or somehow Opened is getting queued on a thread that's not using the async/await model. So can someone help me understand the call stack of the failed request, why it might be different from the one that succeeds, and hopefully how that might explain the missing context?
Web API method:
[HttpGet]
[Infrastructure.Filters.AjaxOnly]
[Route("event/month/list/{year}")]
public async Task<IHttpActionResult> GetRoster___EventMonthItems(int year)
{
try
{
HttpContext.Current.SetCallContextFacilityID(); //This extension method sets the mentioned fallback value for when HttpContext.Current is null
List<RosterDayListItem> data = await _roster___Mapper.GetRoster___EventMonthItems(year);
return Ok(data);
}
catch (Exception ex)
{
Logging.DefaultLogger.Error(ex, "An error occurred while loading report categories.");
return BadRequest(ex.Message);
}
}
EF6 Query
public async Task<List<Roster___EventListItem>> GetRoster___EventListItems(int year, int month)
{
using (var dbContextScope = _dbContextFactory.Create())
{
var context = GetContext(dbContextScope);
var result = await context.DropInEvents
.Where(w => w.EventDate.Year == year && w.EventDate.Month == month && w.IsDeleted == false)
.Select(d => new Roster___EventListItem
{
ID = d.ID,
EventDate = d.EventDate,
EventTime = d.StartTime,
Year = d.EventDate.Year
})
.OrderBy(f => f.EventDate).ThenBy(f => f.EventTime)
.ThenByDescending(f => f.EventDate)
.ToListAsync();
return result;
}
}
Successful call stack:
Call Stack with null contexts:
Update
Grasping at straws but after thinking about it for a while, it seemed like maybe something inside EF 6 is perhaps queueing the call to IDbConnectionInterceptor.Opened on a thread in a way that loses the SynchronizationContext. So I went looking through the EF source following my successful stack trace and it looks like the call to Opened is initiated here in InternalDispatcher.DispatchAsync<TTarget, TInterceptionContext> line 257. I'm not sure how it would explain the intermittency of my problem, but might it have something to do with Task.ContinueWith that is being used here? Interestingly I found this other question related to both Task.ContinueWith that method and a SynchronizationContext being lost. Then i found this question where the answer says it will continue with a ThreadPool thread which will not have an associated SyncrhonizationContext unless one is explicitly specified. So this sounds like what I came looking for, but I'm not sure whether the TaskContinuationOptions.ExecuteSynchronously option used changes anything, and if this is the culprit, I don't yet understand why my HttpContext is available most of the time.

C# EntityFramework IQueryable Memory Leak

We're seeing memory resources not be released:
With the following code using .NET Core:
class Program
{
static void Main(string[] args)
{
while (true) {
var testRunner = new TestRunner();
testRunner.RunTest();
}
}
}
public class TestRunner {
public void RunTest() {
using (var context = new EasyMwsContext()) {
var result = context.FeedSubmissionEntries.Where(fse => TestPredicate(fse)).ToList();
}
}
public bool TestPredicate(FeedSubmissionEntry e) {
return e.AmazonRegion == AmazonRegion.Europe && e.MerchantId == "1234";
}
}
If I remove the test predicate .Where I get a straight line as expected, with the predicate the memory will continue to rise indefinitely.
So while I can fix the problem I'd like to understand what is happening?
EDIT:
Altering the line to:
public void RunTest() {
using (var context = new EasyMwsContext()) {
var result = context.FeedSubmissionEntries.ToList();
}
}
Gives the graph:
So I don't believe this is due to client side evaluation either?
EDIT 2:
Using EF Core 2.1.4
And the object heap:
Edit 3:
Added a retention graph, seems to be an issue with EF Core?
I ended up running into the same issue. Once I knew what the problem was I was able to find a bug report for it here in the EntityFrameworkCore repository.
The short summary is that when you include an instance method in an IQueryable it gets cached, and the methods do not get released even after your context is disposed of.
At this time it doesn't look like much progress has been made towards resolving the issue. I'll be keeping an eye on it, but for now I believe the best options for avoiding the memory leak are:
Rewrite your methods so no instance methods are included in your IQueryable
Convert the IQueryableto a list with ToList() before using LINQ methods that contain instance methods (not ideal if you're trying to limit the results of a database query)
Make the method you're calling static to limit how much memory piles up
I suspect the culprit isn't a memory leak but a rather unfortunate addition to EF Core, Client Evaluation. Like LINQ-to-SQL, when faced with a lambda/function that can't be translated to SQL, EF Core will create a simpler query that reads more data and evaluate the function on the client.
In your case, EF Core can't know what TestPredicate is so it will read every record in memory and try to filter the data afterwards.
BTW that's what happened when SO moved to EF Core last Thursday, October 4, 2018. Instead of returning a few dozen lines, the query returned ... 52 million lines :
var answers = db.Posts
.Where(p => grp.Select(g=>g.PostId).Contains(p.Id))
...
.ToList();
Client evaluation is optional but on by default. EF Core logs a warning each time client evaluation is performed, but that won't help if you haven't configured EF Core logging.
The safe solution is to disable client-side evaluation as shown in the Optional behavior: throw an exception for client evaluation section of the docs, either in each context's OnConfiguring method or globally in the Startup.cs configuration :
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(...)
.ConfigureWarnings(warnings =>
warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
UPDATE
A quick way to find out what's leaking is to take two memory snapshots in the Diagnostics window and check what new objects were created and how much memory they use. It's quite possible there's a bug in client evaluation.

System.InvalidOperationException: The connection was not closed. The connection's current state is connecting

I noticed this exception being thrown in some code someone else wrote and am trying to fix it. We are using ASP.NET MVC with Entity Framework 6.0. I've looked elsewhere but not finding anything that's relevant because the exception in my case is being created by a race condition (rather than an object which isn't cleaned up).
Background:
A js function is iterating through an array and making an ajax call to a controller for each item.
The controller calls a service, and ultimately the repository which is using entity framework + linq, which looks something like this:
public IList<int> GetData(IList<int> ids)
{
var query = from widget in _context.Widgets
.Where(x => x.SourceId == 1 &&
ids.Contains(x.ID))
from widgetSourceType in widget.WidgetSource
.Where(x => x.WidgetSourceType.Name == "foo")
select widget.ID;
return query.ToList();
}
It's bombing out on the query.ToList() line, which I believe is when the query is actually executed. It looks pretty clear to me that the ajax calls are firing multiple operations concurrently, and the second is trying to access the same connection before the first is done with it. But I'm not sure on how to fix. Has anyone had experience with this?
I assume the issue is in _Context. Try to use using statement in order to clear any opened connections.
using (_context conn =
new YourDBContext('connectionString'))
{
conn.Open();
//your query
conn.Close();
}
Or you can use async queries and then use await to get the result.

How can I close connection string object in Entity Framework

Today I got an exception when I run my application from the below code
dbContext.ManageTasks.Where(x => x.IsDeleted == false).ToList();
Error is
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
I got a solution from this discussion : How can I solve a connection pool problem between ASP.NET and SQL Server?
Solution is : if I close current connection string object, then this error will be gone.
SqlConnection myConnection = new SqlConnection(ConnectionString);
myConnection.Open();
// some code
myConnection.Close();
The above code for ADO.NET, not Entity Framework.
How can I close the connection string object in Entity Framework?
I'm a bit unsure as to why you would need the use of "SqlConnection". DbContext is a disposable class. I found the safest way to clean up is by using the "using" tag. E.g.
using(DbContext _context = new MyContext())
{
\\Your Work here
}
This will automatically call _context.Dispose() when it reaches the closing curly bracket which in effect closes your connection.
The alternative would be:
DbContext _context = new MyContext();
_context.ManageTasks.Where(x => x.IsDeleted == false).ToList();
_context.Dispose();
This does mean it's possible to miss the dispose call if an exception occurs.
Hope this helps.
Useful Link
In order to handle transaction scope and to help dispose your objects, you may want to look at the repository pattern and the unitOfWork pattern. Or both.
Further to this, I know use DependencyInjection to handle a lot of connections. I just ensure they are Scoped (per web request) :)
You are probably keeping a global variable dbContext somewhere in your class. You should not do that!
You could use the IDisposable interface that DbContext implements and use using statements. You initialize a new DbContext whenever you need one:
using (YourDbContext dbContext = ...)
{
// do some actions
}
// context will be closed
The benefit of using using is that it will close and dispose, even if the code inside will throw an exception.

Entity Framework with SQLite Error: An error occurred while reading from the store provider's data reader

I'm using SQLite 1.0.89 with EF 5 .NET Framework 4.5 on VS2013 in a WPF application in C#.
The DB size is not big and the table that the program use contain max 1000 row.
using the program I found this error often:
An error occurred while reading from the store provider's data reader.
See the inner exception for details.
the inner exception is :
{"library routine called out of sequence\r\nnot an error"}
Other time the inner exception is:
Connection was closed, statement was terminated
Another time i found:
unknown error\r\nno connection handle available
I found this article searching:
Parallel.Foreach loop creating multiple db connections throws connection errors?
SQL Server CE database size issue
but no one solve my problem.
The query that the program do IS NOT inside a loop, but are single query performed when button is pressed on the UI but i noticed that the error happens more often (bun not only) when I press the query button a lot of time faster.
Other thing. The error happens more often (but again not only) when the DB Context is access via a properties istead of a method example:
public List<Product> ProductList
{
get {
return DBContext.Products.ToList();
}
}
The problem was caused by multiple thread that query the db using the same DBContext.
Using a different DBContext for each thread solve the problem.
In my case I was using Castle Windsor to inject the DBContext into the class that perform the query.
Configuring the lifestyle of the DBContext to one per thread the problem has gone.
I had this error occur in EF6.1 and took me a while to figure out what was going on. Simplified the query, and it worked, so I figured something must be going on in the query. Found out I was querying a string and passing a compare on an int. once I changed the int.toString all worked.
Dim OpenOrder = (From p In context.CP_Carthead Where p.SessionID =
MySessionInfo.Current.LeadID.ToString And p.CustomerID = LeadID
And p.Deleted = False And p.PortalID =
TenantID).OrderBy(Function(p) p.OrderID).FirstOrDefault
OrderID = OpenOrder.OrderID
LeadID (my Session) is an int. without the .ToString I get the error in the above post
I experienced this error today on a production application.
This was caused by a user, for some reasons, installing the program and its dependencies on a OneDrive-enabled folder. This triggered a whole lot of bugs including this one. Hope it saves someone's day.
An unhandled exception occured.An error occurred while reading from the store provider's data reader. See the inner exception for details. at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.HandleReaderException(Exception e)
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.StoreRead()
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

Categories

Resources