我目前使用构建器模式构建我的MVC视图模型。
var viewModel = builder
.WithCarousel(),
.WithFeaturedItems(3),
.Build()
我遇到的问题是我必须对异步方法进行服务调用。这意味着我的构建器方法必须返回Task<HomeViewModelBuilder>
而不是HomeViewModelBuilder
。这使我无法将构建方法链接到await
它们。
示例方法
public async Task<HomeViewModelBuilder> WithCarousel()
{
var carouselItems = await _service.GetAsync();
_viewModel.Carousel = carouselItems;
return this;
}
现在我必须使用await
来调用构建器方法。
await builder.WithCarousel();
await builder.WithFeaturedItems(3);
有没有人使用构建器模式的异步方法?如果是这样,是否可以链接方法或将await
推迟到构建方法。
答案 0 :(得分:5)
我之前并没有真正做到这一点,但这里可以替代Sriram的解决方案。
这个想法是捕获构建器对象中的任务而不是任务的结果。然后Build
方法等待它们完成并返回构造的对象。
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);
}
}
用法:
var viewModel = await builder
.WithCarousel(),
.WithFeaturedItems(3),
.BuildAsync();
此构建器模式适用于任意数量的异步或同步方法,例如:
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);
}
}
用法仍然相同。
答案 1 :(得分:3)
正如我在评论中所说,您可以为HomeViewModelBuilder
以及Task<HomeViewModelBuilder>
编写扩展方法并对其进行链接。
public static class HomeViewModelBuilderExtension
{
public static Task<HomeViewModelBuilder> WithCarousel(this HomeViewModelBuilder antecedent)
{
return WithCarousel(Task.FromResult(antecedent));
}
public static async Task<HomeViewModelBuilder> WithCarousel(this Task<HomeViewModelBuilder> antecedent)
{
var builder = await antecedent;
var carouselItems = await builder.Service.GetAsync();
builder.ViewModel.Carousel = carouselItems;
return builder;
}
public static Task<HomeViewModelBuilder> WithFeaturedItems(this HomeViewModelBuilder antecedent, int number)
{
return WithFeaturedItems(Task.FromResult(antecedent), number);
}
public static async Task<HomeViewModelBuilder> WithFeaturedItems(this Task<HomeViewModelBuilder> antecedent, int number)
{
var builder = await antecedent;
builder.ViewModel.FeaturedItems = number;
return builder;
}
}
我们为单个操作添加了几种方法,以便您可以使用HomeViewModelBuilder
或Task<HomeViewModelBuilder>
进行链接。否则您将无法拨打builder.WithCarousel()
然后像
一样使用它private static void Main()
{
HomeViewModelBuilder builder = new HomeViewModelBuilder();
var task = builder
.WithCarousel()
.WithFeaturedItems(3);
}
答案 2 :(得分:2)
使用构建器模式,您可以创建构建对象的策略。在调用构建方法之前,它不构造对象。如果填充对象的逻辑在构建方法中,那么您可以一起调用所有异步方法。
请参阅下面的构建器示例代码。它只是对概念的一种展示,因此您可能希望对其进行改进。
public class Builder
{
private bool hasCarousel = false;
private int featuredItems = 0;
public Builder WithCarousel()
{
hasCarousel = true;
return this;
}
public Builder WithFeaturedItems(int featured)
{
featuredItems = featured;
return this;
}
public BuiltObject Build()
{
if (hasCarousel)
{
// do carousel related calls
}
if (featuredItems > 0)
{
// do featured items related calls.
}
// build and return the actual object.
}
}
答案 3 :(得分:0)
与async
的交易是它具有连锁效应。它往往会在您的代码中传播,以保持async
"async all the way"。
如果要保留构建器模式(或任何其他流畅模式,如LINQ
),同时保留async
,则每个可能需要async
重载你打算做的电话。否则你无法使用它们,或者使用它们会错误(例如"sync over async")。
async-await
相当新,但我确定随着时间的推移,您几乎可以获得任何异步选项。
答案 4 :(得分:0)
可接受的答案很有帮助,但是我认为在某些情况下(至少在我的情况下),等待构建器本身而不是构建的对象会更有帮助:
public sealed class HomeViewModelBuilder
{
private List<Task<HomeViewModelBuilder>> taskList= new List<Task<HomeViewModelBuilder>>();
public HomeViewModelBuilder WithCarousel(){
taskList.add(WithCarouselInternal());
return this;
}
private async Task<HomeViewModelBuilder> WithCarouselInternal()
{
var result = await _service.GetAsync();
// do something with the result
return this;
}
public HomeViewModelBuilder WithSomthingElse(){
taskList.add(WithSomethingElseInternal());
return this;
}
(...)
public async Task<HomeViewModel> BuildAsync()
{
await Task.WhenAll(taskList);
// On that point we can now be sure that all builds are finished
return new HomeViewModel(...);
}
}
我希望这更通用一些,因为您只需保持taskList为最新状态,并且还可以通过一种简单的方法多次调用相同的构建方法(您不能覆盖以前的对一种构建方法,因为所有任务都将出现在列表中,因此构建肯定会等到Task完成)