根据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
,我想要一个可观察到的:
为此,我想出了类似的东西:
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
完全替换为仅在令牌为空时执行此身份验证的管道。但那是第二步,我在一次失败了:))
所以要回到最初的问题:是否有一种方法可以编写一个管道,无论当前用户数量多少都会执行一次,但如果所有用户断开连接并再次重新连接,则再次执行?
答案 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();
}
}