Rx Window,Join,GroupJoin?

时间:2011-10-10 16:55:37

标签: c# system.reactive

我已经生成/测试了两个Observable来组合执行单个查询。

用户可以拥有多个角色。只要他们的角色id发生变化,就需要更新数据。但是,只有在查询处于活动状态时才会更新数据(当前有一些控件需要数据)。

当暂停查询时,也可能发生角色ID更改。当查询再次变为活动状态时,数据也应加载。

//Tuple has the Id of the current Role and the time that the Id updated
IObservable<Tuple<Guid, DateTime>> idUpdate

//Tuple has the state of the query (true=active or false=suspended)
//and the time the state of the query updated
IObservable<Tuple<bool, DateTime>> queryStateUpdate 

我想创建

//A hot observable that pushes true whenever the query should execute
IObservable<bool> execute 

我将其分解为两个可以合并的案例,但我无法弄清楚如何创建案例observable。

  • 案例a)角色Id更新&amp;最后一个州是活跃的
  • 案例b)状态更新为Active&amp;&amp;这是角色ID更新后的第一个活动状态

我已经浏览了视频,lee campbells网站,初学者TOC等,但我似乎找不到这个rx加入的好例子。关于如何创建执行或案例可观察的任何想法?

3 个答案:

答案 0 :(得分:1)

鉴于所描述的问题 - 由于我没有看到实际的id(Guid)用于什么,也没有DateTime值,这有点模糊 - 我得到了以下内容查询似乎可以解决您的问题:

IObservable<bool> execute =
    idUpdate
        .Publish(_idUpdate =>
            from qsu in queryStateUpdate
            select qsu.Item1
                ? _idUpdate.Select(x => true) 
                : Observable.Empty<bool>())
        .Switch();

我已使用以下idUpdate&amp; queryStateUpdate可观察物。

var rnd = new Random();

IObservable<Tuple<Guid, DateTime>> idUpdate =
    Observable
        .Generate(
            0,
            n => n < 10000,
            n => n + 1,
            n => Tuple.Create(Guid.NewGuid(), DateTime.Now),
            n => TimeSpan.FromSeconds(rnd.NextDouble() * 0.1));

IObservable<Tuple<bool, DateTime>> queryStateUpdate =
    Observable
        .Generate(
            0,
            n => n < 100,
            n => n + 1,
            n => n % 2 == 0,
            n => TimeSpan.FromSeconds(rnd.NextDouble() * 2.0))
        .StartWith(true)
        .DistinctUntilChanged()
        .Select(b => Tuple.Create(b, DateTime.Now));

如果您能就问题提供一些说明,我可能会提供更好的答案以满足您的需求。


编辑:添加了Id在非活动时更改时所需的“重放(1)”行为。

请注意,我已经摆脱了使用DateTime元组的需要。

IObservable<Guid> idUpdate = ...
IObservable<bool> queryStateUpdate = ...

var replay = new ReplaySubject<Guid>(1);
var disposer = new SerialDisposable();
Func<bool, IObservable<bool>, IObservable<Guid>,
    IObservable<Guid>> getSwitch = (qsu, qsus, iu) =>
{
    if (qsu)
    {
        return replay.Merge(iu);
    }
    else
    {
        replay.Dispose();
        replay = new ReplaySubject<Guid>(1);
        disposer.Disposable = iu.TakeUntil(qsus).Subscribe(replay);
        return Observable.Empty<Guid>();
    }
};

var query =
    queryStateUpdate
        .DistinctUntilChanged()
        .Publish(qsus =>
            idUpdate
            .Publish(ius =>
                qsus
                    .Select(qsu =>
                        getSwitch(qsu, qsus, ius))))
        .Switch();

答案 1 :(得分:1)

我在阅读问题时说,有一个通知流idUpdate,只要设置了queryStateUpdate,就会对其进行处理。如果未设置queryStateUpdate,则通知应暂停,直到再次设置queryStateUpdate为止。

在这种情况下,join运算符不会解决您的问题。

我建议您在queryStateUpdate未设置时需要某种形式的缓存,即

List<Tuple<Guid,DateTime>> cache = new List<Tuple<Guid,DateTime>>();
Subject<Tuple<Guid,DateTime>> execute = new Subject<Tuple<Guid,DateTime>>();

idUpdate.Subscribe( x => {
    if (queryStateUpdate.Last().Item1) //might be missing something here with Last, you might need to copy the state out
        exeucte.OnNext(x);
    else
        cache.Add(x);
    });

queryStateUpdate.Subscribe(x=> {
    if (x.Item1)
    {
       //needs threadsafety
       foreach(var x in cache)
           execute.OnNext(x);
      cache.Clear();
    });

答案 2 :(得分:0)

感谢Enigmativity和AlSki。使用缓存我想出了答案。

var execute = new Subject<Guid>();
var cache = new Stack<Guid>();
idUpdate.CombineLatest(queryStateUpdate, (id, qs) => new { id, qs }).Subscribe( anon =>
{
  var id = anon.id;
  var queryState = anon.qs;
  //The roleId updated after the queryState updated
  if (id.Item2 > queryState.Item2)
  {
      //If the queryState is active, call execute
      if (observationState.Item1)
      {
         cache.Clear();
         execute.OnNext(roleId.Item1);
         return;
      }
      //If the id updated and the state is suspended, cache it
      cache.Push(id.Item1);
  }
  //The queryState updated after the roleId
  else if (queryState.Item2 > roleId.Item2)
  {
     //If the queryState is active and a roleId update has been cached, call execute
     if (queryState.Item1 && cache.Count > 0)
     {
         execute.OnNext(cache.Pop());
         cache.Clear();
     }
  }});
相关问题