EF from Task: System.InvalidOperationException: Reader closed [duplicate] - c#

This question already has answers here:
DbContext for background tasks via Dependency Injection
(2 answers)
Closed 5 years ago.
I'm using a Task to send an Email in the background.
Despite the fact that the PDF (included in the mail as attachment) is generated (GeneratePdf()). I get following exception:
Below is the code that calls GetRegistrationOfChild:
EDIT
Below is the code where the dbContext is loaded (Startup.cs, method ConfigureServices). Afterwards its resolved with DI in the constructor.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

There's a lot of problems here, actually. Your core problem is likely inside the SendRegisterConfirmationAsync method. However, you haven't provided the code for that method. If I had to guess, you're using using statements in a not thread-safe way, based on the error message.
However, it's also entirely possible this is just due to handling async improperly. You're calling an async method from a sync method. Since SendEmail returns void, you're either swallowing the return value of the async method or the async method is async void (which you should pretty much never do). In either case, since you're not awaiting the result of the async method, the rest of your code is moving on potentially taking dependencies with it. For example, things like your DbContext are request-scoped, so if that async method utilizes your DbContext, but doesn't finish before the response is sent, the context will be disposed right from under it, causing exceptions. If you use async, you need to go async all the way.
Further, there's really no such thing as a true "background" task in a web application context. Task.Run merely pulls another thread from the same pool your requests are being handled from. You may allow one thread to return, but you're still sitting on another, so the best case scenario is that you've bought yourself nothing. The worst case scenario is that you've now effectively halved your server's throughput and killed your ability to scale.
If you want to do something in the background, offload it to a background process, i.e. outside the context of your web application. You can use something like Hangfire or Revalee.

Related

Making fire-and-forget calls in a thread-safe manner in C#

I have the following situation: I have an ASP.NET Core controller (.NET 5.0), which has an Import endpoint. Upon calling this endpoint it gathers some configuration and calls
MyService.Import(...)
MyService.Import performs some time-consuming operations - it retrieves data from external endpoints (with paging and retries), and then does some heavy lifting on the database.
The controller method can't afford to wait for the service method to finish. It would simply take too long and the callers of my endpoint would get a timeout error. And it's not supposed to anyway, MyService has its own ways of reporting about the status of the import - via logs. This is supposed to be a fire-and-forget call.
I've tried to achieve this fire-and-forget behavior in the following ways:
Make the service method async and just don't await it. The warning I get from this says "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the await operator to the result of the call." So I would expect that to be the behavior - the calling (controller) method continues and reaches return Ok(), while the service method does its thing. This isn't what happens. The controller method just stands still waiting for the service method it ostensibly isn't awaiting.
Await the method, but put ConfigureAwait(false) at the end. Same result as above.
Calling the service method on a different thread via Task.Run(() => MyService.Import(...). This approach actually allows the controller method to finish without waiting for the service method. But this causes errors further down the pipe. Somewhere in the bowels of the code, through many layers of DI-initialized providers and resolvers, something was broken by switching the thread, and I get the following error:
System.ObjectDisposedException: 'Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: MyDbContext
Re-working the infrastructure so that it can handle switching threads is probably a project for a week. Is there a way have the controller method return without threading? Or some way to "pass the entire context" to the new thread?
. But this causes errors further down the pipe. Somewhere in the bowels of the code,
If you kick of an async task it can't access any resources tied to the HTTP request lifecycle, like your DbContext or other scoped services. It needs to create its own scope, or simply create a new instance of your DbContext for the long-running task.
So I dug through the code and my colleague actually solved it the following way:
Response.OnCompleted(async () => MyService.Import(...
return Ok();
This obviously works only for this specific case. I'm not sure how "correct" this is, or if there is some resource on the server that's stuck waiting for MyService.Import, but it does achieve the desired behavior wherein the controller method returns a response without waiting for the service to finish.

Calling async method from sync method is ASP .NET web api - issue

I have a ASP .NET web api 2 application, and I'm trying to call asnyc method from sync method and meanwhile I encountered some issues. If I just call the method as a regular method the body after delay doesn't get executed, if I call it with Task.Run() it gets executed
public void CallingMethod(MethodExecutionArgs args)
{
//do something with the args
System.Diagnostics.Debug.WriteLine("BEFORE ");
WriteFileToDiskAsync(args); // If I run only this, I never see "WriteFile() - AFTER delay" in the output
Task.Run(async () => await WriteFileToDiskAsync(args)); // this executes the entire async method
System.Diagnostics.Debug.WriteLine($"Finally written from sync method");
}
private async Task<bool> WriteFileToDiskAsync(dynamic file)
{
System.Diagnostics.Debug.WriteLine("Before delay inside async");
await Task.Delay(3000);
System.Diagnostics.Debug.WriteLine("WriteFile() - AFTER delay");
}
Result: I always see the lines written from CallingMethod, but when I call the async method like a regular method, I only see the "Before delay inside async", If I call it with Task.Run() it see both lines from the async method.
Question1: Can someone explain this behavior, why doesn't the async method execute fully?
Does it have something to do with what Stephen Cleary says: If you lose your AppDomain for any reason, that in-progress work is lost.
I read these articles but can't figure out why is this happening:
How to call asynchronous method from synchronous method in C#?
Fire and forget async method in asp.net mvc
C# async/await with/without awaiting (fire and forget)
https://blog.stephencleary.com/2012/12/returning-early-from-aspnet-requests.html According to most articles what I'm trying to do is not recommended (among other things the exceptions from the async method would get swallowed) but in my case I already have the information that I need to generate the response and what is done in the Async method doesn't concern the client at all...
Context: What I'm trying to achieve is, on a existing API route that creates a X Resource (saves it in a database), after the X resource is created, I want to asynchronously call a method that will Create a json file with some information from that X Resource and save it in a filesystem. But even if the writing of the file fails, I want to return a successful response to the client (because his request was to actually save the X Resource - which succeeded) he doesn't care about Creating external file in XYZ filesystem.
Question2: What would you say is the best practice to achieve what I described above, considering all the existing methods chained in the CreateResource route are sync?
Question1: Can someone explain this behavior, why doesn't the async method execute fully? Does it have something to do with what Stephen Cleary says: If you lose your AppDomain for any reason, that in-progress work is lost.
No. The AppDomain is still there in this case. In ASP.NET (pre-Core), there is an ASP.NET SynchronizationContext that represents the request. await by default captures the current context and uses that to resume executing its async method.
So, when the await completes, it attempts to resume executing on the SynchronizationContext for that request. However, that request has been completed, and so it doesn't make sense to resume on that context. The resulting behavior is undefined.
Question2: What would you say is the best practice to achieve what I described above, considering all the existing methods chained in the CreateResource route are sync?
If you are sure you want to return early, then you should have a background service handle creating the file, not the ASP.NET service. Since you have a database already, you could use the Outbox Pattern, where you place messages in an "outbox" table as part of the same transaction as your resource creation. Then a separate background service reads the "outbox" table and writes the JSON file.

EF6 Connected Scenario error : A second operation started on this context before a previous asynchronous operation completed

I am doing a Desktop Application in WPF and as it is said in Entity Framework Tutorial, it is a good idea to use the connected scenario.
So in my application, I have a class that has a static property Dal that contain the dbContext and all database access functions.
It was working well until I started to think about adding some async in the Dal, now I sometimes have this error even if it doesn't change the behavior in my application (just going faster as expected):
System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed.
Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context.
Any instance members are not guaranteed to be thread safe.
I checked on every async call functions, everytime I call one, I use await.
I am also using db.SaveChangesAsync() in my Dal's functions.
Maybe I am wrong using a static property for my Dal ?

C#/ASP.NET Core: Is this proper usage of .Wait()?

Context:
We've got a big inheritance tree with interfaces and the whole shabang. We feed a request to a factory, which creates the proper object, then we call a DoStuff() method that all of these objects expose. The method doesn't return anything, just sets some properties.
Problem:
In just a single case, inside the DoStuff() method, we have to call an async method, there's just no way around it because we don't have control over that method. Refactoring the entire inheritance tree to turn the DoStuff() method to use an async Task signature is not something we look forward to.
We're fine with blocking the thread until the operation finishes.
Question:
We've tried using RunSynchronously() on the method but we've got the following error:
System.InvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler, Boolean waitForCompletion)
Should we use Wait() instead?
It depends if you are happy with the downsides. This is a web application, so you want to keep your threads as free as possible, however by using .Wait() you have one additional thread unavailable on top of whatever threads are needed inside the async method for the continuations (or thread bound tasks, again not typical for a web app).
You won't see any deadlocks however, if this was what you were afraid of. ASP.NET Core doesn't use a non-default SynchronizationContext, so you won't lock up, but be warned, it's a slippery slope to not being able to cope under load, as this is worse than the synchronous version.
If this were regular ASP.NET you would see deadlocks if any of the awaits inside of the async method aren't using ConfigureAwait(false) (it's not quite as black and white as that, but that's near enough the truth).

Cancel Long Running Entity Framework 6 async requests

I'm using Entity Framework 6 (DbContext) in a WPF application, and I'd like to find a way to properly cancel the async data load methods (ToListAsync & FirstOrDefaultAsync) so I can immediately start another request.
I've been trying to stick with the single context per form (tab in my case) standard, and so far, I've been dealing with the non-thread safe nature of these calls by ensuring that the UI is disabled during requests, so the user can't start any new requests while one's in progress. However, I've run into a use case now where this is just not possible. I need to keep the UI responsive during some long-running requests, and in order to do that, I need a way to cancel the current request and start another one right away.
I've tried leveraging the CancellationToken support that was added to the Async methods, but I've found that when I cancel the request, it doesn't actually cancel anything. It will properly throw the OperationCanceledException, but the request is still in progress, and when I try to make another request after that, I still get NotSupportedException (A second operation started on this context...)
I'm using change-tracking, so changing the app to use a new Context for every request is not realistic.
Also, I've temporarily gotten around the issue by disposing the current context and creating a new one every time this particular view model makes a request while one's already in progress. This technically solves my issue, but I'm wondering if there's a way to do it while staying with the same context.
So, does anyone have any experience with this? I find it hard to believe I'm the first one who's run into this issue, but all other answers I've found on here for similar questions either recommend I use the CancellationToken (which doesn't work properly) or are a bit older and don't apply to the Async methods.
EDIT 1:
Since no one's answered this yet, I'm really starting to wonder what my options are here. A bit of background. I'm converting a Silverlight application to WPF.The Silverlight application was using WCF RIA services with EF 4.1, but with the WPF application, we decided to just use EF6.1.
With Silverlight & WCF, there is no limit to the number of async calls you can make at time, and we actually have a single context for the entire application (bad, I know, but simple and we never had any issues). We just bind directly to the entities, and use change-tracking to save the changes made by the user.
Is there just no way to do this in WPF, using EF 6.1 and the Async methods, in a real world application, where sometimes, you just need to cancel what the app is in the progress of doing and do what the user wants, without crashing and burning?
Posting my solution for now. I'm not a huge fan, but it's all I've been able to get to work that wouldn't require completely rewriting this application from scratch.
I'm now using the AsyncLock class from AsyncEx in repository methods that are accessing or saving tracked entities. Each of the DbContext objects use their own lock, so I'm not blocking other calls from other contexts or for untracked entities.
As an example, my GetListAsync method in my repository:
public async virtual Task<IList<T>> GetListAsync(IDbContext context,
Expression<Func<T, bool>> where,
IOrderByClause<T>[] orderBy = null,
params Expression<Func<T, object>>[] navigationProperties)
{
using (await context.ContextLock.LockAsync())
{
IList<T> list = await GetListQuery(context, where, orderBy, navigationProperties).ToListAsync();
return list;
}
}
GetListQuery creates the query using the nav properties and where and order by clauses.
I'll probably also add a timeout as well, using a CancellationToken.

Categories