Introduction to EntityFramework 6 Part 4: asynchronous operations and logging
July 21, 2014 Leave a comment
Introduction
In the previous post we looked at CRUD operations in EntityFramework 6. In this post we’ll look at the async coding features of EF.
Asynchronous code execution in .NET has been greatly enhanced by the await-async keywords. If you are not sure what they are about you can start here. They can help keeping the available threads busy thereby utilising the CPU resources better. You have probably seen a lot of methods built-into .NET4.5+ whose names end with “Async”, e.g HttpClient.SendAsync or StreamWriter.WriteAsync.
These awaitable asynchronous methods all return a Task or a Task of T. EntityFramework is no exception to the new trend of making the code more responsive and scalable. The async version of some well-known operators such as ToList() are usually available on methods that return something immediately, i.e. the non-deferred LINQ operators.
Demo
Open the Cars demo application we’ve been working on so far in this series. We have the following Index action in CarController.cs at present:
public ActionResult Index() { return View(db.Cars.ToList()); }
As you type “db.Cars.” in the editor you’ll see that there’s a ToListAsync method. You cannot just use that method like…
public ActionResult Index() { return View(db.Cars.ToListAsync()); }
…since it returns a Task of List of Cars and the view is expecting an IEnumerable of Cars. To make the Index method asynchronous we need to transform it as follows:
public async Task<ActionResult> Index() { return View(await db.Cars.ToListAsync()); }
…where Task resides in the System.Threading.Tasks namespace. If you run the application now you should get the list of cars from the database as before. However, the following is happening behind the scenes:
- Some thread enters the Index action
- The thread sees the await keyword and lets another thread take over the processing of the awaitable method, i.e. the collection of cars from the database
- When the awaitable method is finished by another thread then either the original thread or a new thread finishes the Index method
Check out the above link for a more detailed treatment of the async-await keywords.
Similarly the Details method can be rewritten as follows:
public async Task<ActionResult> Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Car car = await db.Cars.FindAsync(id); if (car == null) { return HttpNotFound(); } return View(car); }
Note the FindAsync method which as the asynchronous version of Find in EF and returns a Task of Car.
The POST Create method can be made asynchronous by calling the async version of SaveChanges, i.e. SaveChangesAsync:
[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Create([Bind(Include="Id,Make,Category")] Car car) { if (ModelState.IsValid) { db.Cars.Add(car); await db.SaveChangesAsync(); return RedirectToAction("Index"); } return View(car); }
In case you’d like to generate asynchronous action methods when inserting a new controller then you can check the following option:
The templating engine will generate code similar to how our existing code was transformed above.
Logging
The DbContext object has a property called Database which in turn has a property called Log. The Log property is of type Action of String, i.e. you can assign a void method to it that accepts a string. The string parameter includes the actual SQL statements sent to the database.
In CarsDbContext.cs we have an empty constructor at present:
public CarsDbContext() : base("DefaultConnection") { }
Add the following code to the body of the constructor:
Database.Log = statements => Debug.WriteLine(statements);
Run the application and navigate to /cars. You should see a couple of SQL statements in the Output window related to the retrieval of Cars from the data store:
The first time a query is run you’ll see one or more statements related to data migration, but they won’t be run on subsequent queries.
You can of course assign a more complicated logging delegate to the Log property: log to the database, a file, GrayLog etc.
In the next post, which will end this introductory series on EF, we’ll look at how to update schemas.
You can view all posts related to data storage on this blog here.