我有以下简化的用例:
我有一个表格。打开表单会告诉用户扫描其论文(条形码)。应当显示每次扫描,以便用户理解“哦,是的,扫描仪可以工作”。 每次扫描时,我都必须查询SAP Web服务,以获取一些信息,例如授权。
所以扫描仪是我的观察者,我有两个观察者
我使用发布将扫描仪实现为可连接的可观察者,以便两个订阅始终获得相同的结果。我不在乎扫描程序是否会立即开始扫描并产生值,只要将每次扫描以正确的顺序推送到每个观察者-甚至是几秒钟后连接的观察者即可。这是一个有效的解决方案:
var scanner = new BarcodeScanner();
var sapBackend = new SapBackend();
var scans = scanner.ListenToScanner().Publish();
Thread.Sleep(TimeSpan.FromSeconds(1));
Print("Starting Subscription 1", ConsoleColor.Red);
var sub1 = scans.Subscribe(val => NotifyUser(val));
Thread.Sleep(TimeSpan.FromSeconds(2));
Print("Starting Subscription 2", ConsoleColor.Green);
var sub2 = scans
.SelectMany(scan => sapBackend.CheckAuthorization(scan))
.Subscribe(answer => GateInIfAllowed(answer));
Print("Starting Producer\n", ConsoleColor.Yellow);
var scannerObservation = scans.Connect();
Thread.Sleep(TimeSpan.FromSeconds(3));
sub1.Dispose();
sub2.Dispose();
scannerObservation.Dispose();
Console.ReadKey();
所以我对此解决方案不满意的是,我不想
我更喜欢使用RefCount的解决方案,以便Observable可以自动处理。 而且我想使用Repeat(),以便在扫描仪已经开始产生一些值之后可以进行订阅。 但是我无法以这种方式编写它,因为Repeat()和RefCount()彼此不喜欢。
完整代码示例
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
namespace RxDemo.PlaygroundConsole
{
internal class Sample4 : IAmASample
{
public void TimeToDemonstrate()
{
var scanner = new BarcodeScanner();
var sapBackend = new SapBackend();
var scans = scanner.ListenToScanner().Publish();
Thread.Sleep(TimeSpan.FromSeconds(1));
Print("Starting Subscription 1", ConsoleColor.Red);
var sub1 = scans.Subscribe(val => NotifyUser(val));
Thread.Sleep(TimeSpan.FromSeconds(2));
Print("Starting Subscription 2", ConsoleColor.Green);
var sub2 = scans
.SelectMany(scan => sapBackend.CheckAuthorization(scan))
.Subscribe(answer => GateInIfAllowed(answer));
Print("Starting Producer\n", ConsoleColor.Yellow);
var scannerObservation = scans.Connect();
Thread.Sleep(TimeSpan.FromSeconds(3));
sub1.Dispose();
sub2.Dispose();
scannerObservation.Dispose();
Console.ReadKey();
}
private static void Print(string message, ConsoleColor color)
{
var col = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ForegroundColor = col;
}
private void GateInIfAllowed(string restriction)
{
Print(restriction, ConsoleColor.Green);
}
private void NotifyUser(string val)
{
Print($"UI NOTIFICATION: you scanned {val}", ConsoleColor.Red);
}
private class SapBackend
{
public IObservable<string> CheckAuthorization(string val)
{
//return GetValueSimulatingWebserviceCall(val).ToObservable();
return Observable
.FromAsync(() => GetValueSimulatingWebserviceCall(val));
}
private Task<string> GetValueSimulatingWebserviceCall(string val)
{
Task.Delay(2000);
var restriction = "can";
if (DateTime.Now.Second % 2 == 0)
restriction = "cannot";
return Observable.Return($"User {restriction} gate in for scan {val}").ToTask();
}
}
private class BarcodeScanner
{
private static readonly Random Random = new Random();
public IObservable<string> ListenToScanner()
{
return Observable
.Interval(TimeSpan.FromSeconds(1))
.Select(_ => RandomString(6))
.Do(val =>
{
var col = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Scanner produced " + val);
Console.ForegroundColor = col;
});
}
private static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[Random.Next(s.Length)]).ToArray());
}
}
}
}