Search code examples
c#.netasynchronousasync-awaitbuilder

Using async Tasks with the builder pattern


I currently use the builder pattern to construct my MVC view models.

var viewModel = builder
                  .WithCarousel(),
                  .WithFeaturedItems(3),
                  .Build()

The problem I am coming up against is when I have to make a service call to an async method. This means that my builder method then has to return Task<HomeViewModelBuilder> instead of HomeViewModelBuilder. This prevents me from chaining the build methods as I have to await them.

Example method

public async Task<HomeViewModelBuilder> WithCarousel()
{   
    var carouselItems = await _service.GetAsync();
    _viewModel.Carousel = carouselItems;
    return this;
}

Now I have to use await to call the builder methods.

await builder.WithCarousel();
await builder.WithFeaturedItems(3);

Has anyone used async methods with the builder pattern? If so, is it possible to be able to chain the methods or defer the await to the build method.


Solution

  • I have not actually done this before, but here's an alternative to Sriram's solution.

    The idea is to capture the tasks in the builder object instead of the result of the tasks. The Build method then waits for them to complete and returns the constructed object.

    public sealed class HomeViewModelBuilder
    {
      // Example async
      private Task<Carousel> _carouselTask = Task.FromResult<Carousel>(null);
      public HomeViewModelBuilder WithCarousel()
      {
        _carouselTask = _service.GetAsync();
        return this;
      }
    
      // Example sync
      private int _featuredItems;
      public HomeViewModelBuilder WithFeaturedItems(int featuredItems)
      {
        _featuredItems = featuredItems;
        return this;
      }
    
      public async Task<HomeViewModel> BuildAsync()
      {
        return new HomeViewModel(await _carouselTask, _featuredItems);
      }
    }
    

    Usage:

    var viewModel = await builder
        .WithCarousel(),
        .WithFeaturedItems(3),
        .BuildAsync();
    

    This builder pattern works with any numbers of asynchronous or synchronous methods, for example:

    public sealed class HomeViewModelBuilder
    {
      private Task<Carousel> _carouselTask = Task.FromResult<Carousel>(null);
      public HomeViewModelBuilder WithCarousel()
      {
        _carouselTask = _service.GetAsync();
        return this;
      }
    
      private Task<int> _featuredItemsTask;
      public HomeViewModelBuilder WithFeaturedItems(int featuredItems)
      {
        _featuredItemsTask = _featuredService.GetAsync(featuredItems);
        return this;
      }
    
      public async Task<HomeViewModel> BuildAsync()
      {
        return new HomeViewModel(await _carouselTask, await _featuredItemsTask);
      }
    }
    

    Usage is still the same.