3

I write web application using ASP.NET MVC WebAPI and I want to transform current synchronous code to asynchronous for optimization. Problem is that I fill ViewModel with multiple objects taken from repository. These calls from repository should be async.

Let's asume I have signature for repository calls respecting this interface

public interface ICompanyRepository
{
    IEnumerable<Company> GetCompanies();
    IEnumerable<Address> GetAddresses();
}

ViewModels definition

public class CompaniesFullViewModel
{
    public IEnumerable<Company> Companies { get; set; }
    public IEnumerable<Address> Addresses { get; set; }
}

And controller:

public class CompanyController
{
    public readonly ICompanyRepository Repository { get; private set; }

    public CompanyController(IRepository repository)
    {
        Repository = repository;
    }

    [ResponseType(typeof(CompaniesFullViewModel))]
    public HttpResponseMessage Get()
    {
        var companies = Repository.GetCompanies();
        var addresses = Repository.GetAddresses();

        HttpStatusCode statusCode = companies.Any()
             ? HttpStatusCode.OK
             : HttpStatusCode.PartialContent;

        return
            Request.CreateResponse(
                statusCode,
                new CompaniesFullViewModel
                {
                    Companies = companies,
                    Addresses = addresses
                });
    }
}

Furthermore I have tests implemented to the controller:

[TestClass]
public sealed class CompanyTestController : BaseTestController
{
    #region Fields

    private static Mock<ICompanyRepository> _repositoryMock;
    private static CompanyController _controller;

    #endregion

    [ClassInitialize]
    public static void Initialize(TestContext testContext)
    {
        // Mock repository
        _repositoryMock = new Mock<ICompanyRepository>();
        DependencyResolver.Default.Container.RegisterInstance(_repositoryMock.Object);

        // Create controller
        _controller =
            DependencyResolver.Default.Container.Resolve<CompanyController>();

        // Init request
        _controller.Request = new HttpRequestMessage();
        _controller.Request.SetConfiguration(new HttpConfiguration());
    }

    [ClassCleanup]
    public static void Cleanup()
    {
        _controller.Dispose();
    }

    [TestMethod]
    public void Get_ActionExecutes_ReturnsEmptyCompaniesViewModel()
    {
        var companies = new List<Company>();
        var addresses = new List<Address>();

        // Setup fake method
        _repositoryMock
            .Setup(c => c.GetCompanies())
            .Returns(companies);
        _repositoryMock
            .Setup(c => c.GetAddresses())
            .Returns(addresses);

        // Execute action
        var response = _controller.Get();

        // Check the response
        Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
    }
}

How can I convert the controller to async, if the repository is async and the signature looks like this:

public interface ICompanyRepository
{
    Task<IEnumerable<Company>> GetCompaniesAsync();
    Task<IEnumerable<Address>> GetAddressesAsync();
}
Andree
  • 1,159
  • 2
  • 17
  • 32
  • Maybe I'm missing something. Doesn't the controller signature just change to `public async Task Get()`. Then you just `await` your now-async repository calls? – Brendan Green Jul 14 '15 at 05:55
  • @Andree: `I want to transform current synchronous code to asynchronous for optimization` You sure that'll do what you want? Remember, "asynchronous" != "faster". – Stephen Cleary Jul 14 '15 at 15:27
  • @StephenCleary I know it doesn't necessarily means faster, but I expect high load on the server with partly long-running queries and so I've decided to run my own benchmark to compare the sync vs. async solution. – Andree Jul 14 '15 at 20:18

1 Answers1

4

What you need to do is change the Controller action to be async as well, and change the return type to Task<>. You can then await your asynchronous repository calls:

[ResponseType(typeof(CompaniesFullViewModel))]
public async Task<HttpResponseMessage> Get() // async keyword. 
{
    var companies = await Repository.GetCompaniesAsync(); // await
    var addresses = await Repository.GetAddressesAsync(); // await

    HttpStatusCode statusCode = companies.Any()
         ? HttpStatusCode.OK
         : HttpStatusCode.PartialContent;

    return
        Request.CreateResponse(
            statusCode,
            new CompaniesFullViewModel
            {
                Companies = companies,
                Addresses = addresses
            });
}

By convention, you can also change the name of the controller action to end in Async as well, although if you are using RESTful conventions and / or Routing attributes, the actual name of the controller action isn't really important.

Testing

I use XUnit and NUnit, but it seems MSTest also supports testing of asynchronous methods, and Moq also provides Async versions of the setups:

[Test]
public async Task Get_ActionExecutes_ReturnsEmptyCompaniesViewModel() // async Task
{
    var companies = new List<Company>();
    var addresses = new List<Address>();

    // Setup fake method
    _repositoryMock
        .Setup(c => c.GetCompaniesAsync())
        .ReturnsAsync(companies); // Async
    _repositoryMock
        .Setup(c => c.GetAddressesAsync())
        .ReturnsAsync(addresses); // Async

    // Execute action
    var response = await _controller.Get(); // Await

    // Check the response
    Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
    _repositoryMock.Verify(m => m.GetAddressesAsync(), Times.Once);
    _repositoryMock.Verify(m => m.GetCompaniesAsync(), Times.Once);
}

As an aside, it seems you are using Setter Dependency injection. An alternative is to use Constructor injection, which has the benefit of ensuring that the class is always in a valid state (i.e. there is no transient state while it is waiting for the dependencies to be set). This also allows the dependencies (your repository in this case) to be made readonly.

Community
  • 1
  • 1
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • Thanks for the answer. I actually use contructor injection, I've just didn't provided the constructor. But thank you for the notice. – Andree Jul 14 '15 at 07:22
  • Welcome :) I saw the `public ICompanyRepository` property with a public setter and just assumed setter injection. As an aside, in theory, you could also [asynchronously parallel](http://www.tugberkugurlu.com/archive/how-and-where-concurrent-asynchronous-io-with-asp-net-web-api) up the 2 repository calls with `Task.WhenAll`, however this would require that the repository instance is thread safe, which might not be the case. If parallelism is desirable, the alternative would be to inject a `RepositoryFactory` and then create two separate Repo instances for the 2 calls. – StuartLC Jul 14 '15 at 07:25
  • 1
    Thanks for the article. Really helped me summarize the facts about this issue. – Andree Jul 14 '15 at 20:17