Back to: Entity Framework Tutorials For Begineers and Professionals
Asynchronous Programming with Entity Framework
In this article, I am going to discuss Working with Asynchronous Programming with Entity Framework. Please read our previous article where we discussed how to work with Disconnected Entities in the Entity Framework with Examples. Asynchronous operations are used to avoid blocking a thread while the query is executed in the database. At the end of this article, you will understand how to perform Asynchronous CRUD Operations using the Entity Framework. We are going to work with the same example that we created in our Introduction to Entity Framework Database First Approach article. Please read our introduction to Entity Framework Database First article before proceeding to this article.
Why do we need Asynchronous Programming?
Asynchronous Programming has been introduced in .NET Framework 4.5. Asynchronous Programming allows executing operations in the background so that the main thread can continue to execute its own operations. In this way, the main thread can keep the user interface responsive while the background thread is processing the task in the background.
In simple words, we can say that Asynchronous Programming in .NET Application allows us to handle the threads of our processes in a more efficient way. The idea is to avoid blocking a thread while waiting for a response, either from an external system such as a Web service, or Database Call or from the computer’s file management system.
For example, if we have a web application, it will be able to serve more HTTP requests at the same time by using Asynchronous Programming. This is because each HTTP request is handled by a thread, and if we avoid blocking threads, then there will be more threads available to process more HTTP requests.
Entity Framework 6.X supports Asynchronous Operations for both querying and saving data. That means Entity Framework allows us to execute a query asynchronously using an instance of DbContext class. The advantages of using Asynchronous Programming in an application are as follows:
- It will make your application more responsive to user interactions.
- It will improve the overall performance of your application by avoiding blocking threads.
To work with asynchronous programming in C# we need to use async and await keywords. The idea is that we need to use the async keyword to mark a method as asynchronous and with await, we can wait for an asynchronous operation in such a way that the original thread is not blocked.
Using Async/Await in Entity Framework:
In the same DbContext object, the async and await keywords are defined by the .NET framework with which we can easily perform asynchronous programming. The asynchronous methods are useful for both desktops as well as web applications. In order to understand, how to use async and await in C#, please have a look at the below image. In the below code, you can see, we are calling ToListAsync asynchronous method and we are also using the await keyword so that the Main thread will release and do other tasks. And when the ToListAsync method completes its execution, then one thread will come from the thread pool and will start executing the rest code.
As you can see in the image, we use two keywords i.e. async and await to perform asynchronous operations. We have also used some LINQ queries in the code that we will explain later part in this article.
Asynchronous Query in Entity Framework
For a better understanding of the asynchronous queries, please have a look at the below image which is an example of the async method that executes a LINQ query asynchronously and returns the result.
As you can see in the above image, the GetStudent(int StudentId) method is declared with the async keyword, which makes the GetStudent(int StudentId) method an asynchronous method. The return type of the asynchronous method must be Task (if the method not returning anything) and Task<T> if it is returning a value of type T. As the GetStudent() method is going to return an object of the Student entity, so the return type here is Task<Student> type.
Further, if you notice in the above code, the LINQ query is also marked with the await keyword. This will release the calling thread which executes the method so that the calling thread can do some other tasks. The idea of using await keyword is to release the calling thread until it executes the query and returns the result. Here, we have used FirstOrDefaultAsync asynchronous extension method to get the result. You may use other async methods such as SingleOrDefaultAsync, ToListAsyn, etc. that we will see later part of this article. So, once the query execution is completed, then one of the threads from the thread pool will come and execute the rest of the code of the GetStudent method.
Example to Understand Asynchronous Query in Entity Framework:
Whatever so far we have discussed is shown in the below example. The following example code is self-explained, so please go through the comment lines.
using System; using System.Data.Entity; using System.Threading.Tasks; using System.Threading; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Thread.CurrentThread.ManagedThreadId will return the currently executing unique thread id Console.WriteLine($"Main Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); var query = GetStudent(2); Console.WriteLine($"Main Method Doing Some Other Task by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Here, we are blocking the Main thread to get the result from the GetStudent Method var student = query.Result; Console.WriteLine($"Name: {student?.FirstName} {student?.LastName}"); Console.WriteLine($"Main Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Console.Read(); } private static async Task<Student> GetStudent(int StudentId) { Console.WriteLine($"GetStudent Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Student student = null; using (var context = new EF_Demo_DBEntities()) { Console.WriteLine($"FirstOrDefaultAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); student = await (context.Students.FirstOrDefaultAsync(s => s.StudentId == StudentId)); Console.WriteLine($"FirstOrDefaultAsync Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"GetStudent Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); return student; } } }
Output:
As you can see in the above output, the Main method is started and completed by a thread whose Id is 1. But please focus on the GetStudent method. It is started by the same Thread whose Id is 1. But once it starts executing the FirstOrDefaultAsync asynchronous method, the Thread whose Id is 1 is released and that thread starts executing the other code in the Main method. Once the query execution is completed, another thread whose Id is 5 will come from the thread pool and start executing the rest of the code inside the GetStudent method.
Asynchronous Save using Entity Framework
The Entity Framework provides an asynchronous version of the SaveChanges method called the SaveChangesAsync() to save entities into the database asynchronously. For better understanding, please have a look at the below image which shows an example of saving a Student Entity to the database asynchronously. In this case, when we call the SaveChangesAsync() method, the calling thread will be released and will do some other task. Once the SaveChangesAsync() method execution is completed, then another thread pool thread will come and execute the rest of the code of the SaveStudent async method.
Example to Understand Asynchronous Save in Entity Framework:
Whatever so far we have discussed is shown in the below example. The following example code is self-explained, so please go through the comment lines.
using System; using System.Threading.Tasks; using System.Threading; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Thread.CurrentThread.ManagedThreadId will return the currently executing unique thread id Console.WriteLine($"Main Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); var student = new Student() { FirstName = "James", LastName = "Smith", StandardId = 1 }; //As the SaveStudent async method not returing anything, so we cannot call the result property SaveStudent(student); Console.WriteLine($"Main Method Doing Some Other Task by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"Main Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Console.Read(); } private static async Task SaveStudent(Student newStudent) { Console.WriteLine($"SaveStudent Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); using (var context = new EF_Demo_DBEntities()) { context.Students.Add(newStudent); Console.WriteLine($"SaveChangesAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Calling the SaveChangesAsync Method, it will release the calling thread await (context.SaveChangesAsync()); Console.WriteLine($"SaveChangesAsync Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"SaveStudent Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } } }
Output:
As you can see in the above output, the Main method is executed by one thread, and the SaveStudent method is executed by two threads. This is possible because when the SaveChangesAsync method is called, it releases the calling thread, and when the SaveChangesAsync method completed its execution, another thread from the thread pool will come and executes the rest of the code.
Using Both Get Async and Save Async in Entity Framework
We have already discussed two examples. In the first example, we get the result using a query and in the second example, we save the student entity using the async query. The following example is a combination of both. In the following example, first, we execute an async query to get the result and then modify the student entity and finally save that modified student entity using the async query. In the below example, first, we called the async GetStudent(int StudentId) method and stored the reference in the query variable. This will start to execute the GetStudent() method but frees the calling thread so that it can execute further statements in the AsyncQueryAndSave method. The query.Result statement will block the current thread execution until the asynchronous GetStudent(int StudentId) complete its execution. Once it completes, then we are updating the FirstName and LastName properties and calling the SaveStudent async method. In the same way, the asynchronous SaveStudent method is called and gets the result.
using System; using System.Threading.Tasks; using System.Data.Entity; using System.Threading; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Thread.CurrentThread.ManagedThreadId will return the currently executing unique thread id Console.WriteLine($"Main Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); AsyncQueryAndSave(1); Console.WriteLine($"Main Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Console.Read(); } public static void AsyncQueryAndSave(int StudentId) { Console.WriteLine($"AsyncQueryAndSave Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); var query = GetStudent(StudentId); Console.WriteLine($"AsyncQueryAndSave Do something else untill get the GetStudent query result by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Wait till get the GetStudent result var student = query.Result; //Once you get the result update the entity student.FirstName = "Steve"; student.LastName = "Smith"; //Call the SaveStudent Method to Save the Updated Student Entity in the database var studentSaveQuery = SaveStudent(student); Console.WriteLine($"AsyncQueryAndSave Do something else untill save the modified student entity by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Wait till get the SaveStudent result var saveStudentResult = studentSaveQuery.Result; //Once you get the result print the message Console.WriteLine($"Student Entity : {saveStudentResult}"); Console.WriteLine($"AsyncQueryAndSave Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } private static async Task<Student> GetStudent(int StudentId) { Console.WriteLine($"GetStudent Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Student student = null; using (var context = new EF_Demo_DBEntities()) { Console.WriteLine($"FirstOrDefaultAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Calling the FirstOrDefaultAsync Method, it will release the calling thread student = await (context.Students.FirstOrDefaultAsync(s => s.StudentId == StudentId)); Console.WriteLine($"FirstOrDefaultAsync Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"GetStudent Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); return student; } private static async Task<string> SaveStudent(Student newStudent) { Console.WriteLine($"SaveStudent Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); using (var context = new EF_Demo_DBEntities()) { //As newStudent is a Disconnected Entity, so we need to set State Manually //Setting the Entity State Based on the StudentID Property Value //If StudentId == 0, Means It is a New Entity, So, Set the Entity State as Added //If StudentId != 0 or > 0, Means It is an Existing Entity, So, Set the Entity State as Modified context.Entry(newStudent).State = newStudent.StudentId == 0 ? EntityState.Added : EntityState.Modified; Console.WriteLine($"SaveChangesAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Calling the SaveChangesAsync Method, it will release the calling thread await (context.SaveChangesAsync()); Console.WriteLine($"SaveChangesAsync Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"SaveStudent Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); return "Student Saved Successfully"; } } }
Output:
As you can see in the above output, three threads are executing our application code.
Asynchronous Delete in Entity Framework
In this case, first, you need to Remove the entity from the context object which will mark the entity state as Deleted and when we call the SaveChangesAsync() method, it will remove the entity from the database asynchronously. For a better understanding, please have a look at the following example. The following example code is self-explained, so please go through the comment lines.
using System; using System.Threading.Tasks; using System.Threading; namespace DBFirstApproach { class Program { static void Main(string[] args) { //Thread.CurrentThread.ManagedThreadId will return the currently executing unique thread id Console.WriteLine($"Main Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); var deleteAsyncQuery = DeleteStudentAsync(23); Console.WriteLine($"Main Method Doing Some other task by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Wait till the DeleteStudentAsync Method completed its execution var deleteAsyncResult = deleteAsyncQuery.Result; Console.WriteLine($"DeleteStudentAsync Method Result: {deleteAsyncResult}"); Console.WriteLine($"Main Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); Console.Read(); } public static async Task<string> DeleteStudentAsync(int StudentId) { Console.WriteLine($"DeleteStudentAsync Method Start by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); using (var context = new EF_Demo_DBEntities()) { Console.WriteLine($"FindAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Calling the FindAsync Method, it will release the thread Student DeleteStudent = await context.Students.FindAsync(StudentId); //Then remove the entity by calling the Remove Method which will mark the Entity State as Deleted context.Students.Remove(DeleteStudent); Console.WriteLine($"SaveChangesAsync Method Started by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); //Calling the SaveChangesAsync Method, it will release the thread await (context.SaveChangesAsync()); } Console.WriteLine($"DeleteStudentAsync Method Completed by ThreadId: {Thread.CurrentThread.ManagedThreadId}"); return "Student Deleted Successfully"; } } }
Output:
Note: If the student you are trying to delete has any foreign key relationship data, then it will not allow you to delete the student. In such cases first, you need to delete foreign key table data and then delete the student data. In most of the real-time applications, we never delete any data from the database. In most of the cases we some kind of flag such IsActive which indicates whether the record is active or inactive.
Points to Remember while working with Asynchronous Programming:
The following two important points to remember while working with Asynchronous Programming.
- The Asynchronous Programming is primarily focused on freeing up the current managed thread (thread running .NET code) to do other taask while it waits for the operation to complete which doesnot have any link with the .NET Code. For example, if the database engine is processing a query there is nothing to be done by .NET code.
- In Desktop Applications (WinForms, WPF, Console, etc.) the current thread can be used to keep the UI responsive while the async operation is performed. In Web Applications (ASP.NET, ASP.NET MVC, ASP.NET Web API, etc.) the thread can be used to process other incoming requests. The more number of threads are available, the more number of HTTP Requests then can handle.
In the next article, I am going to discuss Bulk Insert, Update, and Delete in Entity Framework i.e. How to add, remove, and update multiple entities in Entity Framework. Here, in this article, I try to explain Working with Asynchronous Programming with Entity Framework and I hope you enjoyed this Asynchronous Programming with Entity Framework article. Please give your valuable feedback and suggestions about this article.
when I run the AsyncQueryAndSave method it does not update the existing student, instead adds a new student