将发布,RefCount和重复与Rx.NET结合使用

时间:2019-12-06 21:22:59

标签: c# system.reactive rx.net

我有以下简化的用例:

我有一个表格。打开表单会告诉用户扫描其论文(条形码)。应当显示每次扫描,以便用户理解“哦,是的,扫描仪可以工作”。 每次扫描时,我都必须查询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();

Output of this program

所以我对此解决方案不满意的是,我不想

  1. 显式连接到Connectable Observable
  2. 必须存储connect()的IDisposable
  3. 必须在connect()之前向每个观察者预订

我更喜欢使用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());
            }
        }
    }
}

0 个答案:

没有答案