In this post, we are going to convert the synchronous code to asynchronous inside ASP.NET Core. First, we are going to learn a bit about asynchronous programming and why should we write async code. Then we are going to use our project from the .NET Core series and rewrite it in an async manner.

In Part 4 of the series, we have created Generic Repository Pattern and in Part 5 and Part 6 the Controller with Actions that consumes that repository. We recommend reading those articles if you are not familiar with Generic Repository Pattern or if you find any of the concepts in this article hard to understand. Those articles will help you follow along with this article much easier because we won’t dive into its business logic.

We are going to modify the code, step by step, to show you how easy is to convert the synchronous code to an asynchronous one. Hopefully, this will help you understand how asynchronous code works and how to write it from scratch in your applications.

To download the source code for our starting project, you can visit our GitHub repo for the starting project.

For the finished project refer to our GitHub repo for the finished project.

We are going to cover the following sections in this article:

What is Asynchronous Programming

Async programming is a parallel programming technique, which allows the working process to run separately from the main application thread. As soon as the work completes, it informs the main thread about the result, whether it was successful or not.

By using async programming, we can avoid performance bottlenecks and enhance the responsiveness of our application.

How so?

Because we are not sending requests to the main thread and blocking it while waiting for the responses anymore (as long as it takes). Now, when we send a request to the main thread, it delegates a job to a background thread, thus freeing itself for another request. Eventually, a background thread finishes its job and returns it to the main thread. Then the main thread returns the result to the requester.

It is very important to understand that if we send a request to an endpoint and it takes the application three or more seconds to process that request, we probably won’t be able to execute this request any faster in async mode. It is going to take the same amount of time as the sync request.

The only advantage is that in the async mode the main thread won’t be blocked three or more seconds, and thus it will be able to process other requests.

Here is the visual representation of the asynchronous workflow:

Asynchronous Generic Repository

Now that we cleared that out we can learn how to implement the asynchronous code in .NET Core.

Async, Await Keywords and Return Types

The async and await keywords play a crucial part in the asynchronous programming. By using those keywords, we can easily write asynchronous methods without too much effort.

For example, if we want to create a method in an asynchronous manner, we need to add the async keyword next to the method’s return type:

By using the async keyword, we are enabling the await keyword and modifying the way of how method results are handled (from synchronous to asynchronous):

In asynchronous programming we have three return types:

  • Task<TResult>, for an async method that returns a value
  • Task, to use it for an async method that does not return a value
  • void, which we can use for an event handler

What does this mean?

Well, we can look at this through the synchronous programming glasses. If our sync method returns  int then in the async mode, it should return Task<int>, or if sync method returns IEnumerable<string> then the async method should return Task<IEnumerable<string>>.

But if our sync method returns no value (has a void for the return type), then our async method should usually returnTask. This means that we can use the await keyword inside that method but without the return keyword.

You may wonder, why not returning Task all the time? Well, we should use void only for the asynchronous event handlers which require a void return type. Other than that, we should always return a Task.

From C# 7.0 onward, we can specify any other return type, if that type includes GetAwaiter method.

Now, when we have all the information, let’s do some refactoring in our completely synchronous code.

Overview of the IRepositoryBase Interface and the RepositoryBase Class

Our complete repository is interface based, so let’s take a look at our base interface. In the Contracts project open the IRepositoryBase.cs file. We can see different method signatures:

It is important to notice the FindAll and FindByCondition method signatures. Both of them return IQueryable that allows us to attach async calls to them.

The Create, Update, and Delete methods  don’t modify any data, they just track changes to an entity and wait for the EF Core’s SaveChanges method to execute. So, they stay unchanged as well.

Let’s just take a look at the RepositoryBase class:

This class is the implementation of the previous interface and it is a base class for accessing the data from the database.

Modifying the IOwnerRepository Interface and the OwnerRepository Class

Now, let’s continue on the other parts of our repository. In the Contracts project, there is also the IOwnerRepository interface with all the synchronous method signatures which we should change too.

So let’s do that:

So, we just change signatures of the first three GET methods by assigning the Task<TResult> to the return type. We don’t have to change the other three methods because, as we said, they just change the state of the entity and wait for the SaveChanges to execute.

Now, we have to implement this interface by using the async and await keywords. Using the await keyword is not mandatory though. Of course, if we don’t use it, our async methods will execute synchronously, and that is not our goal here.

So, in accordance with the interface changes, let’s modify our OwnerRepository.cs class, that we may find in the Repository project:

IRepositoryWrapper and RepositoryWrapper Changes

We have to modify the Save method in the mentioned interface and the class as well:

And let’s just modify the Save method in the RepositoryWrapper class:

Now, we can continue to the controller modification.

Controller Modification

Finally, we need to modify all of our actions in the OwnerController to work asynchronously.

So, let’s first start with the GetAllOwners method:

We haven’t changed much in this action. We’ve just changed the return type and added the async keyword to the method signature. In the method body, we can now await the GetAllOwnersAsync() method. And that is pretty much what we should do in all the actions in our controller.

So let’s modify all the other actions.






Excellent. Now we are talking async 😀

We can make our actions even more maintainable and readable by implementing Global Error Handling to remove try-catch blocks and Action Filters as well to remove validation code repetitions.


There you go. We have seen how easy is to convert the synchronous repository to asynchronous and how easy is to write async code overall. With a couple of changes, we gave more breathing space to our API and created a more responsive application.

It is a good practice to write async methods (when we have an opportunity) that handles the I/O actions because it is easy to write those methods and the benefits are indisputable.

Thank you for reading the article and we hope you found something useful in it.

If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published .NET Core content we encourage you to subscribe to our blog.