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.
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.