【问题标题】:Forcing a syncronous reactive extension call强制同步响应式扩展调用
【发布时间】:2015-09-30 08:25:15
【问题描述】:

我正在编写一些看起来像这样的代码:

public class ManagerViewModel : ManagerViewModelBase
{
  public ManagerViewModel(ISettingsProvider settingsProvider):
    base(settingsProvider.Settings)
}


public class ManagerViewModelBase
{
  private IEnumerable<string> _mySettings = Enumerable.Empty<string>();

  public ManagerViewModelBase(IEnumerable<string> settings)
  {
    _mySettings = settings;
  }
}

ISettingsProvider 是通过 Unity 提供的,如下所示:

public class SettingsProvider : ISettingsProvider 
{
  private readonly Service _service;
  IEnumerable<string> _settings = Enumerable.Empty<string>();

  public IEnumerable<string> Settings {get {return _settings}};

  public SettingsProvider (
    IService _service,
    ISettingsProvider settingsProvider)
  {
    _service = service;
    LoadSettings();
  }

  private void LoadSettings()
  {
    //_service just supplies the data              
    _service.GetSettings.Subscribe(LoadSettingsCompleted);
  }

  private void LoadSettingsCompleted(IEnumerable<string> settings)
  {
    _settings = settings;
  }
}

问题在于,在实例化 SettingsProvider 时,可能需要一些时间才能从服务调用中获取结果,因此,在调用完成并填充 _settings 之前,会进行 base(settingsProvider.Settings) 调用, 基本上是传递一个空的枚举。

有没有办法让 SettingsProvider.Settings 属性调用等到来自 Service.GetSettings 调用的数据可用,并在一定时间后超时?我想我正在寻找最简单的方法来阻止该属性调用。

谢谢。

【问题讨论】:

  • 如果您还没有给我们提供关键代码的定义,我们该如何提供帮助? _service.GetSettings? IService 的定义将是您问题的一个很棒的补充。
  • 话说这段代码看起来很同步。通过添加异步响应式框架调用,您看到了什么优势?
  • 您可能还想看看这个:blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple,如果异步性很重要,为什么不考虑直接在接口本身上公开它作为Task&lt;IEnumerable&lt;string&gt;&gt; GetSettings 或类似的 - 如果您只返回一次设置。顺便说一句,我过去曾将设置直接公开为可观察对象,以便在成功的应用程序中立即获取对它们的更改。
  • @JamesWorld 我很想了解您过去如何将应用程序设置公开为可观察对象。

标签: c# system.reactive


【解决方案1】:

我意识到您的问题是如何等待/阻止,但我必须告诉您,您可能不应该这样做。您的 ViewModel 很可能是在 UI 线程中创建的,而阻塞是您最不想要的。

如果您的 ViewModel 依赖于异步的东西,那么它的创建是异步的,您应该通过等待隐藏它。 无法使用构造函数中的异步,但您也可以将 VM 构造函数设为私有,并改为公开异步静态工厂方法,例如:

private ManagerViewModel(IEnumerable<string> settings) : base(settings) {}

public async Task<ManagerViewModel> CreateAsync(ISettingsProvider settingsProvider) {
    var settings = await settingsProvider.GetSettingsAsync();
    return new ManagerViewModel(settings);
}

// ....

public async Task<IEnumerable<Settings>> GetSettingsAsync() {
    return await _service.GetSettings().Timeout(...).FirstAsync();
}

我的建议是始终公开异步并且永远不要阻塞。

【讨论】:

    【解决方案2】:

    这些都不是非常反应性,但您可以使用Observable.FirstAsync()Observable.Wait() 扩展方法阻止。因此,您的 LoadSettings() 方法可能类似于:

    private void LoadSettings()
    {
        _settings = _service.GetSettings
                            .Timeout(_expireTime, Observable.Return(Enumerable.Empty<string>()))
                            .FirstAsync()
                            .Wait();
        _service.GetSettings.Subscribe(LoadSettingsCompleted);
    }
    

    【讨论】:

    • @Gluck 您的编辑阻止订阅从_service.GetSettings 返回的 IObservable。如果没有这个,_settings 字段将永远不会更新为新设置。
    • 对,我没想到设置 observable 正在运行更新,并认为它提供了一个值然后完成,但是在再次阅读作者的问题后,这个事实似乎不再清楚
    • @Gluck 好的,我已经回滚了。我 100% 同意您的回答:“我意识到您的问题是如何等待/阻止,但我必须告诉您,您可能不应该这样做。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多