Async 4 - Refactoring application to use Async Await

Step by step refactoring application layers for async/await

Tue, 19 Feb 2019

Intro

This post is part of 4 post blog series. In this series i will start with an introduction about the asynchronous programming and then we will explore more details about the asynchronous programming and it benefits then finally we will go through how we can start using asynchronous programming in our applications. This is the Fourth and Final Post in the series.

Below is the list of all the posts from the series

  1. What is Asynchronous execution Real life and C# example
  2. Why you should you consider it as a web developer?
  3. How async/await works Details of StateMachine working
  4. Demo: Refactoring an application to be asynchronous(This Post)

The await Begins …

This will be the last post of the blog series about asynchronous programming. I may write more about asynchronous programming as i learn more and feel community can benefit from learnings. In this post we will finally work on an application that looks more like a real world application. Instead of some console application with http calls.

I named this post The await begins... because i hope this series will help you to start using asynchronous programming in you applications if you are not doing it already. Without any further delay let’s get started.

First of all i want to give credit where it is due. This post is inspired from the Pluralsight video series from John Papa and Dan Wahlin. If this post result in any copyright breach Please get in touch using twitter DM @NotRanjeet.

In this post as a starting point we will have a very basic Asp.net Core application that uses Entity Framework to access SqlLite Db. We will start with this application written in synchronous manner with the all the database calls being executed synchronously. We will refactor this application to make all the database calls asynchronous. After all the database calls are refactored to be asynchronous we will take a look at how the Unit Test will be impacted by making our code asynchronous in nature.

To grab a copy of the source code for this post please go to Github Link

What we are building

Now if you have copy of the source from the above we are all set to get started with the refactoring. Lets start by looking what we are trying to build.

We have very basic book store implementation.We are using SqlLite as a DB to store out data to minimize the setup you need to up and running with this demo. You can easily replace SqlLiteDB with any Relational database of your choice.

The Different entities and their relations are as below.

  • One Book can have multiple authors and one author can write multiple books so many to many relation between author and book.
  • One Book can have multiple Genres and One Genre can have multiple books so many to many relation between book and Genre.
  • One book can have only one publisher but one publisher can publish many books One to Many relation between Publisher and Book.

Project Structure

I tried to keep the database schema and entities simple as the focus is on using asynchronous programming in your application.

The project structure is very simple. I kept it simple intentionally instead of creating projects for different layers. Project Structure

Refactoring The Repositories

Starting from the Database Layer by looking at the Repositories where we make the database calls. Currently all the calls to fetch data from database are synchronous in nature. So at runtime we call the database for results and while the database return the results we block the execution thread.

Lets have a look at the methods from BookRepository.cs This repository provide methods to fetch books from the database.

 public List<Book> GetBooks()
        {
            //ToList is synchronous call to the database to get books
            return _context.Books
                           .OrderBy(i => i.Id)
                           .ToList();
        }

Now we will try to convert this call into asynchronous operation

public async Task<List<Book>> GetBooksAsync()
        {
            //ToListAsync is asynchronous variant for ToList
            return await _context.Books
                           .OrderBy(i => i.Id)
                           .ToListAsync();
        }

Things to Notice:

  • Method Return Type Changed to Task<List<Book> instead of List<Book>
  • async got added to method signature
  • ToList replaced with ToListAsync.
  • Renamed method to GetBooksAsync not required but good practice to name you async method explicitly

Now we can go forward with the same approach and refactor all the remaining methods.

There is a method under the StoreRepository that will benefit a lot from this async refactoring. Here is the initial synchronous version.

public ReferenceData GetReferenceData()
        {
            try
            {
                var authors = _context.Authors.ToList();
                var genres = _context.Genres.ToList();
                var publishers = _context.Publishers.ToList();
                return new ReferenceData
                {
                    Authors = authors,
                    Genres = genres,
                    Publishers = publishers
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message, ex);
                return null;
            }
        }

After Refactoring this method to asynchronous it becomes.

public async Task<ReferenceData> GetReferenceDataAsync()
        {
            try
            {

                var authors = _context.Authors.ToListAsync();
                //We start Genre Database fetch before we get the result for Authors
                var genres = _context.Genres.ToListAsync();
                //We start Publishers Database fetch before we get the result for Genres
                var publishers = _context.Publishers.ToListAsync();
                //So instead of executing these queries in serial we can trigger them in parallel
                await Task.WhenAll(authors, genres, publishers);
                return new ReferenceData
                {
                    Authors = authors.Result,
                    Genres = genres.Result,
                    Publishers = publishers.Result
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message, ex);
                throw;
            }
        }

Then we can follow the similar pattern and refactor other methods from the repository layer. Next we will look at how the controller actions will be impacted if we refactor them to be asynchronous.

Refactoring API Controllers

Now we will try to refactor the api controllers to consume the Async version of the repository methods. What i did as a part of refactoring instead of editing or deleting old synchronous repository i created asynchronous version of the repositories.

So i will do the same for the controllers instead of editing existing sync methods i will create async version of the controller methods. So you have two examples in parallel to compare in the final project files.

Here is a synchronous version of Api endpoint to get all the books from database.

//Get api/books
        [HttpGet]
        public ActionResult GetBooks()
        {
            try
            {
                var books = _bookRepository.GetBooks();
                return Ok(books);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message, ex);
                return BadRequest(new { Status = false });
            }
        }

Now after refactoring the controller action to use the asynchronous variant of repository the method will look like.

//Get api/async/books
        [HttpGet]
        public async Task<ActionResult> GetBooks()
        {
            try
            {
                var books = await _bookRepository.GetBooksAsync();
                return Ok(books);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message, ex);
                return BadRequest(new { Status = false });
            }
        }

Things to Notice are:

  • async Keyword in the method signature.

  • Return type changes to Task<ActionResult>

  • Call to repository method is used with await

  • Use the asynchronous version of the repository.

Refactoring Unit Test

Now we have seen how we can refactor our repository and controller actions to utilise the asynchronous programming. But we have not looked at how the unit tests for your controller actions will be impacted by this refactoring.

I have two Unit Tests first one is for testing the Synchronous Controller action and second unit test is to test the Asynchronous controller action.

//Synchronous Unit Test

[Test]
        public void TestSyncMethod()
        {
            //Arrange 
            //GetTestBooks return some Test BooksDto List 
            //We set repo mock to return the test data.
            bookRepoMock.Setup(i => i.GetBooks()).Returns(GetTestBooks());
            var booksRepo = bookRepoMock.Object;
            var logger = loggerMock.Object;
            var booksController = new BookApiController(booksRepo, logger);

            //Act
            var result = booksController.GetBooks();

            //Assert
            Assert.IsInstanceOf<OkObjectResult>(result);
            var okData = result as OkObjectResult;
            Assert.IsInstanceOf<List<BookDto>>(okData.Value);
            var list = okData.Value as List<BookDto>;
            Assert.AreEqual(1, list.Count);
        }

Now we will look at the Unit Test for testing asynchronous controller action.

 [Test]
        public async Task TestAsyncMethod()
        {
            //Arrange 
            //GetTestBooks return some Test BooksDto List 
            //We set repo mock to return the test data.
            //NOTE: When we setup mock we Use Task.FromResult instead of plain List
            bookAsyncRepoMock.Setup(i => i.GetBooksAsync()).Returns(Task.FromResult(GetTestBooks()));
            var booksRepoAsync = bookAsyncRepoMock.Object;
            var logger = loggerMock.Object;
            var booksController = new BookApiControllerAsync(booksRepoAsync, logger);

            //Act
            //NOTE:We await the Controller Action Call
            var result =await booksController.GetBooks();

            //Assert
            Assert.IsInstanceOf<OkObjectResult>(result);
            var okData = result as OkObjectResult;
            Assert.IsInstanceOf<List<BookDto>>(okData.Value);
            var list = okData.Value as List<BookDto>;
            Assert.AreEqual(1, list.Count);
        }

Few things to notice to highlight the difference between the approach to unit test the asynchronous methods.

  • Test method signature include async keyword in it.
  • We use Task.FromResult to setup the return type on Mock repository.
  • We await the call to Controller action.
  • Everything after await to Controller Action is business as normal.

See you soon

Thanks for reading. This is the last post from the asynchronous programming series. I hope it was worth your time to read through the posts. If you have comments or feedback please let me know my Twitter DMs are open. If you think anyone in your network can benefit from these post. Please share with them.

Loading...
Ranjeet Singh

Ranjeet Singh Full Stack Developer (.NET, ReactJS, Redux, Azure) You can find me on twitter @NotRanjeet or on LinkedIn at LinkedIn