为什么在所有初始订户断开连接后RefCount无法正常工作? (终极版)

时间:2016-03-03 02:29:23

标签: c# .net system.reactive

根据Lee Campbell的要求,这是this original的后续问题。它旨在在我试图解决的用例的上下文中提出问题。

我有一个WebApiService包装原始Web API并提供令牌管理。也就是说,它会跟踪身份验证令牌,并将其传递给原始API。以下是WebApiService

中公开方法之一的示例
public IObservable<User> UpdateUserAsync(int id, UpdateUserRequest request) =>
    this
        .EnsureAuthenticatedAsync()
        .SelectMany(
            _ =>
                this
                    .rawWebApi
                    .UpdateUserAsync(this.TokenValue, id, request));

尽可能地,它只需在转发到原始网址API之前调用EnsureAuthenticatedAsync,然后使用this.TokenValue传递令牌。

EnsureAuthenticatedAsync方法如下所示:

public IObservable<Unit> EnsureAuthenticatedAsync() =>
    this
        .Token
        .FirstAsync()
        .SelectMany(token => string.IsNullOrWhiteSpace(token) ? this.authenticate : Observable.Return(Unit.Default));

我原来的问题是由于我尝试编写身份验证管道(上面的this.authenticate)而引发的。请注意,这是使用单个observable替换整个EnsureAuthenticatedAsync方法的第一步。

对于authenticate,我想要一个可观察到的:

  1. 在有人订阅(冷/懒)
  2. 之前什么都不做
  3. 只做一次工作,即使一次有多个订阅者
  4. 如果所有订户都断开连接,则
  5. 再次工作
  6. 为此,我想出了类似的东西:

    this.authenticate = Observable
        .Defer(() =>
            Observable
                .Create<Unit>(
                    async (observer, cancellationToken) =>
                    {
                        while (!cancellationToken.IsCancellationRequested)
                        {
                            var result = await this
                                .authenticationService
                                .AuthenticateAsync(this);
    
                            if (result.WasSuccessful)
                            {
                                observer.OnNext(Unit.Default);
                                observer.OnCompleted();
    
                                return;
                            }
                        }
                    }))
        .Publish()
        .RefCount();
    

    这里的想法是允许对WebApiService方法的任意数量的同时调用导致执行单个身份验证循环。一旦通过身份验证,所有订阅者都将完成,任何未来的订阅者都意味着我们需要再次重新进行身份验证,从而重新执行延迟的可观察对象。

    当然,上述可观察到的问题与我原始问题中的简化问题相同:一旦延迟的observable完成一次,Publish将立即完成任何未来的可观察(尽管延迟观察被重新请求)。

    如上所述,我的最终目标是将EnsureAuthenticatedAsync完全替换为仅在令牌为空时执行此身份验证的管道。但那是第二步,我在一次失败了:))

    所以要回到最初的问题:是否有一种方法可以编写一个管道,无论当前用户数量多少都会执行一次,但如果所有用户断开连接并再次重新连接,则再次执行?

1 个答案:

答案 0 :(得分:0)

可观察序列不能多次完成。您要在此处删除OnCompleted来电,以便authenticate无法完成多次,并将.Take(1)添加到EnsureAuthenticatedAsync以便订阅authenticate将在一个值之后完成。

下面是一个工作控制台应用程序。将obs1(包含Take)的引用替换为obs以重现您的问题。在这两种情况下,您都可以快速按Enter键以使所有四个订阅者获得相同的值。

class Program
{
    static int value = 0;

    static void Main(string[] args)
    {
        var obs = Observable.Create<int>(observer =>
        {
            Console.WriteLine("Generating");

            Interlocked.Increment(ref value);

            return Observable.Return(value)
                .Delay(TimeSpan.FromSeconds(1))
                .Subscribe(observer);
        })
        .Publish() 
        .RefCount();

        var obs1 = obs.Take(1);

        obs1.Subscribe(
            i => Console.WriteLine("First {0}", i), 
            () => Console.WriteLine("First complete"));
        obs1.Subscribe(
            i => Console.WriteLine("Second {0}", i), 
            () => Console.WriteLine("Second complete"));

        Console.ReadLine();

        obs1.Subscribe(
            i => Console.WriteLine("Third {0}", i), 
            () => Console.WriteLine("Third complete"));
        obs1.Subscribe(
            i => Console.WriteLine("Fourth {0}", i), 
            () => Console.WriteLine("Fourth complete"));

        Console.WriteLine("Press enter to exit");
        Console.ReadLine();
    }
}