ASP.NET MVC WebAPI create ViewModel from async tasks

I am writing a web application using ASP.NET MVC WebAPI and I want to convert the current synchronous code to asynchronous for optimization. The problem is that I am filling the ViewModel with multiple objects taken from the repository. These calls from the repository must be asynchronous.

Suppose I have a signature for repository requests related to this interface

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

      

Definition of ViewModels

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

      

And the 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
                });
    }
}

      

Also, I have tests implemented by 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 asynchronous and the signature looks like this:

public interface ICompanyRepository
{
    Task<IEnumerable<Company>> GetCompaniesAsync();
    Task<IEnumerable<Address>> GetAddressesAsync();
}

      

+3


source to share


1 answer


What you need to do is change the Controller action to async

and change the return type to Task<>

. Then you can wait for your asynchronous calls to the repository:

[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 it in async

, although if you're using RESTful conventions and / or routing attributes, the actual name of the controller action doesn't really matter.

Testing



I use XUnit

and NUnit

, but it seems MSTest also supports testing asynchronous methods , and Moq provides t23 versions as well:

[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 looks like you are using Setter Dependency Injection. An alternative is to use constructor injection, which has the advantage of ensuring that the class is always in a valid state (i.e., there is no transition state while it is waiting for dependencies to be installed). It also allows you to make it dependencies (your repository in this case) readonly

.

+3


source







All Articles