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;
Related
I want to run a method that uses an injected DBContext to make db calls but I want to use several tasks running asynchronously to call said method to make the program run faster. I tried using Parallel.ForEach but got a "A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext" error, realizing that dbcontext isn't thread-safe.
Is there a way to create tasks in a for loop, call the method in question that uses the injected DBContext, but is thread safe? I'm trying to import data into a CRM, and this would significantly speed up the process. I could use a batch insert, but there's a lot of entity associations that would make that very difficult.
I realize there's no code I provided, I'm just wondering if there's a way to even go about doing this, wherein async tasks are created and can hit the DBContext without multiple threads crisscrossing.
I know that this action method in the asp.net controller class can handle thousands of concurrent requests using only a few threads even if _httpClient.GetAsync takes many seconds to return. That's because HttpClient.GetAsync() (in windows) uses I/O completion ports and no thread is running until GetAsync() returns.
[HttpGet]
public async Task<object> GetData()
{
var result = await _httpClient.GetAsync("https://stackoverflow.com");
return result.StatusCode;
}
But can we say the same for async database operations? Is EF Core using some thread-less async or a new thread will be created for every await SaveChangesAsync() call?
[HttpPost]
public async Task<ActionResult<object>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return ok;
}
When the database is in some remote machine will it leverage some sockets IOCP so we can use the thread for a smaller interval (only for non-network operations in db)?
If a new thread is used for the whole time of the DB operation then I don't see any scalability improvement because for every request I finish the thread that started the Controler/Action and I just start a new (DB) one.
In Winforms or WPF we have a usability improvement because the UI needs to remain responsive but in Asp.Net Core there is no SynchronizationContext or UI to worry about.
I see async/await is used in more places in asp.net core (i.e. razor await Component.InvokeAsync) but is it really useful?
But can we say the same for async database operations?
Generally, yes. Both the HTTP client and DB query are I/O-based operations, so they don't require a local thread to just sit there waiting for the response.
Is EF Core using some thread-less async or a new thread will be created for every await SaveChangesAsync() call?
The specific implementation depends on the EF Core DB provider. Most of them are async these days but there are some notable exceptions like SQLite (and possibly Oracle? I don't remember). The common MSSQL provider is fully async, i.e., not using a separate thread behind the scenes.
When the database is in some remote machine will it leverage some sockets IOCP so we can use the thread for a smaller interval (only for non-network operations in db)?
DB queries are similar to HTTP in the sense that the client sends the query and then the DB responds with the results. There aren't any "non-network operations" from the client side.
Now, if you're asking about the DB server, then that's an implementation detail and doesn't affect how EF Core (or any other DB client) operates at all.
If a new thread is used for the whole time of the DB operation then I don't see any scalability improvement because for every request I finish the thread that started the Controler/Action and I just start a new (DB) one.
This is the exact reason why Task.Run is discouraged on ASP.NET.
I see async/await is used in more places in asp.net core (i.e. razor await Component.InvokeAsync) but is it really useful?
Razor is a bit more questionable in my mind, but that's coming from a design perspective rather than an efficiency perspective. Async razor allows the page itself to do things like query a db. I prefer to have the action perform all the async work and pass a view model to the page, which is then rendered synchronously since all the data is already in-memory. But that's just a design preference.
As a final note, async on the web server helps your web server scale. It doesn't change the scalability of your db server at all. If you only have one db server, then allowing your web server to scale probably won't buy you any scalability overall since your bottleneck is likely your db server. Async db calls benefit primarily from cloud dbs or db clusters. That said, if this is new development, then there's no reason not to make the web server async even if it's not the bottleneck. Further discussion in this article.
I am using Async and await with multi threading.
If I use Async and await on single thread it works fine but when I use multiple threads, it breaks with an error that I am trying to access dbcontext with multiple threads.
I know I can't do that. But now I want have scheduler which will schedule access of dbcontext to each thread.
How can I code such kind of scheduler/ mutex or whatever that solves this issue.
You can definitely use EF with async/await, but you can't perform two operations at once on the same context.
In terms of performance, it's actually faster to create two contexts which looks to be the reason you're using async/await.
For this example, I'd recommend just creating two separate contexts for each CallToDbOps().
This is not so much of an answer as information to help [User]...
Have a read of this blog also have a read of this article about Entity Framework specifications for its async pattern support
DbContext is not thread-safe
You must never access your DbContext-derived instance from multiple threads simultaneously. This might result on multiple queries being sent concurrently over the same database connection. It will also corrupt the first-level cache that DbContext maintains to offer its Identity Map, change tracking and Unit of Work functionalities.
In a multi-threaded application, you must create and use a separate instance of your DbContext-derived class in each thread.
So if DbContext isn't thread-safe, how can it support the async query features introduced with EF6? Simply by preventing more than one async operation being executed at any given time (as documented in the Entity Framework specifications for its async pattern support). If you attempt to execute multiple actions on the same DbContext instance in parallel, for example by kicking off multiple SELECT queries in parallel via the the DbSet.ToListAsync() method, you will get a NotSupportedException with the following message:
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.
Entity Framework's async features are there to support an asynchronous programming model, not to enable parallelism.
Taken from EF article:
"Thread Safety
While thread safety would make async more useful it is an orthogonal feature. It is unclear that we could ever implement support for it in the most general case, given that EF interacts with a graph composed of user code to maintain state and there aren't easy ways to ensure that this code is also thread safe.
For the moment, EF will detect if the developer attempts to execute two async operations at one time and throw."
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 : EntityFramework (6) and async ( waitingForActivation)?
However, it does not address awaiting multiple items, just one. My goal was to accomplish something along these lines
var car = db.Cars.ToListAsync();
var people = db.People.ToListAsync();
Task.WhenAll(car,people);
Unfortunately I got this runtime exception (I should have known really)
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.
Database contexts aren't thread safe. Okay. So now I am considering factoring out the calls to be methods and then awaiting both method calls (or an easy way to demo this would be to just wrap each of the db calls shown above in using(db){} statements). Regardless, the problem is that with that pattern it will require a new DbContext for each ToListAsync.
Will using a DbContext per ToListAsync call be a threat to a connection pool? Is this an anti pattern?
Will using a DbContext per ToListAsync call be a threat to a connection pool?
It will use one connection per DbContext. Depending on your scenario, this may oversubscribe, or it may not. It depends a lot on the expected usage and the capabilities of your database.
Is this an anti pattern?
Not necessarily. This puts more strain on the DB, as it's going to perform both queries simultaneously. The upside is you're potentially increasing the throughput on the client side (in this case, ASP.NET being the client to the DB), which may outweigh the extra strain. Measuring is really the only way to know whether the benefits outweigh the strain on the DB.