将async Tasks与构建器模式一起使用

时间:2014-08-14 07:17:22

标签: c# .net asynchronous async-await builder

我目前使用构建器模式构建我的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推迟到构建方法。

5 个答案:

答案 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;
    }
}

我们为单个操作添加了几种方法,以便您可以使用HomeViewModelBuilderTask<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完成)