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();
}
3
  • Maybe I'm missing something. Doesn't the controller signature just change to public async Task<HttpResponseMessage> Get(). Then you just await your now-async repository calls? Commented Jul 14, 2015 at 5:55
  • @Andree: I want to transform current synchronous code to asynchronous for optimization You sure that'll do what you want? Remember, "asynchronous" != "faster". Commented Jul 14, 2015 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. Commented Jul 14, 2015 at 20:18

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. I actually use contructor injection, I've just didn't provided the constructor. But thank you for the notice.
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 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.
Thanks for the article. Really helped me summarize the facts about this issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.