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.
Related
I come to you for some advice. I'm developing a console application (server) requiring an external library.
I use Entity Framework Core. But I still have gaps regarding the concept of async / await. I have read quite a few things on it, tried quite a few things.
I set up an Entity / DAL / BLL architecture concerning the database and the ECS (Entity Component System) architecture.
I have systems that call an event (OnInitialized).
In this event, I need to load different data from database (depending on the system).
It's not possible to make several parallel queries simultaneously on same DbContext. I have only one database with multiple tables.
So should I develop interaction with the database fully sync?
Or is there a pattern to use multiple async in same time?
Entity Framework DbContext is not thread safe. So you shouldn't use DbContext concurrency. But you still can use async methods for improving perfomance in .NET Core Web API apps.
The async/await pattern is only indirectly connected to parallelism.
I think you first need to understand exactly what is happening, when you are "await"ing a Task to return. The await-keyword is a bit misleading in my opinion. It does not really wait for the function to return, instead it starts the function that you called in a separate thread, saves the current thread state in a state-engine and causes the program execution path to return control to the caller. Once the called function is finished, that state-engine reconstructs the threadstate and continues execution.
Parallelizing Tasks is actually pretty simple. Instead of a "await"ing for a function to return, you just grab the task-objects that the functions are returning, collect them in a Collection and call Task.WaitAll(collection). After that, all task-objects will have a result-property containing their results.
Create a separate DbContext instance (for example using a DbContext factory) for each parallel query. Start the queries in parallel, then await them as needed in your processing logic.
using var dbContext1 = factory.CreateDbContext();
var queryTask1 = (from e in dbContext1.Entities select e).ToListAsync();
using var dbContext2 = factory.CreateDbContext();
var queryTask2 = (from e in dbContext2.Entities select e).ToListAsync();
var query1Results = await queryTask1;
var query2Results = await queryTask2;
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.
I know you should only use async for stuff which is not "CPU-intensive", e.g. file writes, web calls etc. and therefore I also know it doesn't makes sense to wrap every method into Task.Run or something similar.
However what should I do when I know a method does a web call, but it doesn't offer an async interface. Is it in this case worth to wrap it?
Concrete example:
I'm using CSOM (Client SharePoint Object Model) in my WebApi application (server) and want to get a SharePoint list.
This is normally done like this:
[HttpGet]
[Route("foo/{webUrl}")]
public int GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
context.ExecuteQuery();
return web.Lists.Count;
}
}
And I thought about changing it to something like this:
[HttpGet]
[Route("foo/{webUrl}")]
public async Task<int> GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
await Task.Run(() => clientContext.ExecuteQuery());
return web.Lists.Count;
}
}
Does it make sense and does it help? As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
But is it worth it and should it be done like this?
If so:
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
edit:
updated to use Task.Run as suggested in comment.
However what should I do when I know a method does a web call, but it doesn't offer an async interface.
Unfortunately still somewhat common. As different libraries update their APIs, they will eventually catch up.
Is it in this case worth to wrap it?
Yes, if you're dealing with a UI thread. Otherwise, no.
Concrete example... in my WebApi application (server)
Then, no, you don't want to wrap in Task.Run. As noted in my article on async ASP.NET:
You can kick off some background work by awaiting Task.Run, but there’s no point in doing so. In fact, that will actually hurt your scalability by interfering with the ASP.NET thread pool heuristics... As a general rule, don’t queue work to the thread pool on ASP.NET.
Wrapping with Task.Run on ASP.NET:
Interferes with the ASP.NET thread pool heuristics twice (by taking a thread now and then releasing it later).
Adds overhead (code has to switch threads).
Does not free up a thread (the total number of threads used for this request is almost equal to just calling the synchronous version).
As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
Yes, but all you're doing is jumping threads, for no benefit. The thread used to block on the query result is one less thread ASP.NET has to use to handle requests, so freeing up one thread by consuming another isn't a good tradeoff.
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
Some of the "older" MS APIs just haven't gotten around to adding async versions yet. They certainly should, but developer time is a finite resource.
This is my personal view of your problem and for me the above way is not required. When we host your API in IIS, the server assigns one thread from thread pool it has in the server. The IIS also has a setting of maxConcurrentRequestsPerCPU maxConcurrentThreadsPerCPU. You can setup these values to serve the request instead of handling the request all by yourself.
I've not dealt much with Async/threads/Tasks other than some web services.
I'm using MVC4. I have existing code which takes some time to run. It is using an existing method in the service layer, which uses various other the areas in further layers.
Essentially I was hoping to be able to make an ASync call from the Asynccontroller to that method. However it appears that I would need to change/create another method to implement all the Task & await keywords, quite a hefty job altering all the way down the chain.
Is it possible to call/'fire' a synchronous method in this manner?
I want the long process (creating some documents in the background) to continue running even if the user closes their browser. However if the user still has the browser open then I would like to return a notification to them.
Is there a better way to fire a background task to execute from the MVC Application?
I think you're trying to use async for something it cannot do. As I describe on my blog, async does not change the HTTP protocol.
Is it possible to call/'fire' a synchronous method in this manner?
Sort of. You can use Task.Run if you have CPU-bound work that you want to move off the UI thread in a desktop/mobile application. But there is no point in doing that in an ASP.NET MVC application.
I want the long process (creating some documents in the background) to continue running even if the user closes their browser. However if the user still has the browser open then I would like to return a notification to them.
The problem with this is that you'd be returning early from an ASP.NET request, and (as I describe on my blog), that's quite dangerous.
A proper solution would be to queue the work in a reliable queue (e.g., Azure queue or MSMQ), have an independent backend for processing (e.g., Azure worker role / web job or Win32 service), and use something like SignalR for notification.
As soon as you attempt to do work in an ASP.NET process without a request context, then you run into the danger that your process may exit without completing the work. If you are OK with this, then you can use the BackgroundTaskManager type from my blog above to minimize the chance of that happening (but keep in mind: it can still happen).
Background (Skip this part if you want)
Feel free to skip over this part if you choose, it's just some background for those who want to better understand the problem
At the beginning of one action on my site, I kick off several asynchronous operations. The action returns before the operations are complete. This is what I want. However, the View that gets loaded by this action invokes several other actions in a different controller. Some of these actions rely on the results of the async calls from the first page, so I need to be able to wait on the async calls to finish from the other controller. I thought about just using Session to store the WaitHandles, but as WaitHandles aren't serializable, I obviously can't do that.
Short version:
I need to be able to store an async WaitHandle object somewhere from one controller, such that it can be reliably retrieved in a different controller. These WaitHandles also need to be user-specific, but I can handle that part. Just don't list an option that would make doing that impossible.