Search code examples
c#observablereactive-programmingsystem.reactiveobserver-pattern

.NET Rx C# Observable.FromEventPattern does not run OnCompleted


I could not figure it out why the following code could not run the OnCompleted, can anyone please help me thanks.

Basically what I'm doing is everytime I press a key, I'll fire an event and convert that event to an observable so that I can subscribe an observer to it. The observer simply prints out a text. Also is it a good idea to dispose the subscription once the observable has been consumed (which means call the Dispose inside the do while loop)?

I'm using .Net Rx via Nuget - System.Reactive (6.0.0)

This is the screenshot shows the result on terminal enter image description here

//Create observable from Observable.FromEventPattern
public class MainApp
{
    public event EventHandler<DeviceEventArgs>? DeviceEvent;
    private ConsoleKeyInfo ch;

    public void Start()
    {
        string[] deviceNames = ["Loop", "Sick", "RFID Reader", "Barrier"];
        string[] deviceStatus = ["On", "Off"];
        var rand = new Random();

        IDisposable devEventSubscription;
        var deviceEventObservables = Observable.FromEventPattern<EventHandler<DeviceEventArgs>, DeviceEventArgs>(
                h => DeviceEvent += h,
                h => DeviceEvent -= h)
            .Select(ep => ep.EventArgs);
        do
        {
            Console.WriteLine("Press space bar or enter to exit the program");
            ch = Console.ReadKey();
            Console.WriteLine();

            // Assume ch is data stream from the device that need to convert to Observables
            Console.WriteLine($"Char {ch.KeyChar} was entered");

            devEventSubscription = deviceEventObservables.Subscribe((eventArgs) =>
            {
                Console.WriteLine($"Received an observable. Time: {eventArgs.Epoch}. Type: {eventArgs.DeviceName}. Status: {eventArgs.Status}");
            },
            () => Console.WriteLine("Completed"));

            if (DeviceEvent != null)
            {
                DeviceEvent(null, new DeviceEventArgs()
                {
                    Epoch = DateTimeOffset.Now.ToUnixTimeSeconds(),
                    Status = deviceStatus[rand.Next(deviceStatus.Length)],
                    DeviceName = deviceNames[rand.Next(deviceNames.Length)]
                });
            }
            // Question: why i dont' see the Completed message from the OnCompleted and
            // should i move this line out of the do while loop?
            devEventSubscription?.Dispose();

        } while (!char.IsWhiteSpace(ch.KeyChar));
        
        Console.ReadKey();
    }
}


class Sample
{
    private static void Main()
    {
        var m = new MainApp();
        m.Start();
    }
}

Solution

  • The event never completes because that's not a concept which exists with events. I.e. there is no way for an event to express that it will no longer produce updates. Disposing a subscription to an observable does not imply that the observable has completed, simply that you are no longer observing it.

    As an aside you should only subscribe to the observable once and not inside the loop. The same would be true for disposing the subscription.