I am writing some code using RX in C# that must interface with an older system by emitting events.
In summary, I have an observable and need to emit one event when the observable completes and another event if a timeout exception is detected. The main problem is how best to handle the exception.
I'm relatively new to RX, so although I have found a solution, I can't be sure that there isn't a better or more appropriate way that uses the RX extensions better.
This is not the real code but indicates the pattern of my thinking:
public delegate void SuccessHandler(object sender, SuccessEventArgs e);
public event SuccessHandler OnSuccess;
public delegate void TimeoutHandler(object sender, TimeoutEventArgs e);
public event TimeoutHandler OnTimeout;
var id;
var o = Observable.Return() // <- this would be a fetch from an asynchronous source
.Where(r=>r.status=="OK")
.Timeout(new Timespan(0,0,30)
.Do(r=> {
id=r.Id // <-- Ugh! I know this shouldn't be done!
}
.Subscribe(r => {
var statusResponse= new StatusResponse()
{
Id = r.Id
Name = r.Name
Message = "The operation completed successfully",
Status = Status.Success
};
if (OnSuccess == null) return;
OnSuccess (this, new SuccessEventArgs(statusResponse);
},
e =>
{
_logger.LogError(e, "A matching response was not returned in a timely fashion");
if (OnTimeout == null) return;
OnTimeout(this, new TimeoutEventArgs(id));
});
If I didn't need to detect and act upon the timeout it would be fine; I have already worked out how to substitute the Subscribe for ToEventPattern:
...
.Select(r =>
{
var statusResponse= new StatusResponse()
{
Id = r.Id
Name = r.Name
Message = "The operation completed successfully",
Status = Status.Success
};
return new EventPattern<SuccessEventArgs>(this, new SuccessEventArgs(statusResponse));
})
.ToEventPattern();
However, I'd like to be able to detect the timeout (and possibly other exceptions). my experiments with Catch have been unsuccessful because I can't seem to get the types to line up correctly, probably because I don't really understand what is going on.
I'd very much appreciate opinions on this. Is this an acceptable solution? How can I improve it? Can anyone point me to some good online references that will explain how this kind of flow-control and exception handling can be done (all the examples I've seen so far seem to stop short of the real-world case where you want to emit an event and combine that with exception handling).
Thanks in advance
You can branch from observables quite easily, e.g.
var a = Observable.Range(0, 10);
var b = a.Select(x => x * x);
var c = a.Select(x => x * 10);
A word of warning - if the observable is cold, this will cause the producer function to run for each subscription. Look up the difference between hot and cold observables if this isn't clear.
I've created a solution that creates two branches from the source observable and turns each into an event:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var service = new Service();
var apiCall = service.CallApi();
apiCall.OnSuccess.OnNext += (_, __) => Console.WriteLine("Success!");
apiCall.OnTimeout.OnNext += (_, __) => Console.WriteLine("Timeout!");
Console.ReadLine();
}
}
class SuccessEventArgs{}
class TimeoutEventArgs{}
class ApiCall
{
public IEventPatternSource<SuccessEventArgs> OnSuccess {get;}
public IEventPatternSource<TimeoutEventArgs> OnTimeout {get;}
public ApiCall(IEventPatternSource<SuccessEventArgs> onSuccess, IEventPatternSource<TimeoutEventArgs> onTimeout)
{
OnSuccess = onSuccess;
OnTimeout = onTimeout;
}
}
class Service
{
public ApiCall CallApi()
{
var apiCall = Observable
.Timer(TimeSpan.FromSeconds(3))
.Do(_ => Console.WriteLine("Api Called"))
.Select(_ => new EventPattern<SuccessEventArgs>(null, new SuccessEventArgs()))
// .Timeout(TimeSpan.FromSeconds(2)) // uncomment to time out
.Timeout(TimeSpan.FromSeconds(4))
// the following two lines turn the "cold" observable "hot"
// comment them out and see how often "Api Called" is logged
.Publish()
.RefCount();
var success = apiCall
// ignore the TimeoutException and return an empty observable
.Catch<EventPattern<SuccessEventArgs>, TimeoutException>(_ => Observable.Empty<EventPattern<SuccessEventArgs>>())
.ToEventPattern();
var timeout = apiCall
.Materialize() // turn the exception into a call to OnNext rather than OnError
.Where(x => x.Exception is TimeoutException)
.Select(_ => new EventPattern<TimeoutEventArgs>(null, new TimeoutEventArgs()))
.ToEventPattern();
return new ApiCall(success, timeout);
}
}
Related
We use BufferBlocks to build a small simulation tool where we want to find areas that takes a long time to complete. Producers and Consumers of the blocks will essentially sleep for x amount of time and then post a message to another block.
We decided to use an Observer pattern. Howver, I see some behavior I did not expect. Whenever the OnNext method of the observers is called the BufferBlock is empty (Count == 0). This is problematic as I want only 1 observer to be able to fetch the value from the queue.
Is there a way to change this behavior? If not, how should I handle consumption from the BufferBlocks?
Currently I want to be able to do something similar to post the messages and have all Observers try to fetch it:
public void OnNext(Object value)
{
var res =this.AsConsumer().ConsumeQueue.ReceiveAsync().Result;
Thread.Sleep(this.TimeToConsume );
ProduceQueue.Post(someOtherValue);
}
I have written some tests to show the behavior of the BufferBlock.
[Test]
public void
WhenObservingMocks_CallsOnNextForAllMocks()
{
var firstObserver = new Mock<IObserver<int>>();
var secondObserver = new Mock<IObserver<int>>();
var block = new BufferBlock<int>();
block.AsObservable().Subscribe(firstObserver.Object);
block.AsObservable().Subscribe(secondObserver.Object);
block.Post(2);
Thread.Sleep(TimeSpan.FromMilliseconds(50));
firstObserver.Verify(e => e.OnNext(It.IsAny<int>()), Times.Once);
secondObserver.Verify(e => e.OnNext(It.IsAny<int>()), Times.Once);
}
[Test]
public void
WhenHavingObservers_DoesConsumesTheElementFromQueue()
{
var firstObserver = new Mock<IObserver<int>>();
var secondObserver = new Mock<IObserver<int>>();
var block = new BufferBlock<int>();
block.AsObservable().Subscribe(firstObserver.Object);
block.AsObservable().Subscribe(secondObserver.Object);
block.Post(2);
Assert.Zero(block.Count);
}
[Test]
public void
WhenPostingOnce_CanOnlyReceiveOnce()
{
var block = new BufferBlock<int>();
block.Post(2);
Assert.True(block.TryReceive(out int _));
Assert.False(block.TryReceive(out int _));
}
Following would NOT catch the emitted value:
someObservable = Observable.FromEventPattern(this, "MyEvent");
FireMyEvent("foo");
await someObservable.FirstOrDefaultAsync(e => e.Args == "foo");
So I've come across this pattern in my unit tests:
var tsc = new TaskCompletionSource<bool>();
var callback = someObservable.FirstOrDefaultAsync(...);
using (callback.Subscribe(e => tsc.SetResult(e.Args)))
{
FireMyEvent("foo");
tsc.Task.Wait(1000);
}
Assert.AreEqual("foo", tsc.Task.Result);
Basically I need to subscribe, then trigger the action and then await the subscribed observable.
Is it possible to simplify this subscribe/await without introducing new Task, or WaitHandle?
I'm not sure what exactly you're trying to do, but if I understand the basic idea then this works:
var subject = new Subject<Unit>();
var callback = subject.FirstOrDefaultAsync();
using (callback.Subscribe(_ => { }, () => Console.WriteLine("Done.")))
{
subject.OnNext(Unit.Default);
}
Can you explain your use-case more fully?
I don't really understand your assumption, that the await won't catch the result.
You have to await the event before it gets fired
someObservable = Observable.FromEventPattern(this, "MyEvent");
Task.Delay(1000).ContinueWith(_ => FireMyEvent("foo"));
var result = await someObservable.FirstOrDefaultAsync(e => e.Args == "foo");
Your result ist now a EventPattern. However I would recommend a more type safer FromEventPattern-Overload:
var eventObservable = Observable
.FromEventPattern<YourEventArgs>(
h => MyEvent += h,
h => MyEvent -= h);
I need to have a hot observable which wraps a price feed. This is subscribed in multiple areas.
The observable is created using Refcount and passed around for subscription.The first subscriber calls subscribe hence starts the stream and get all the events. The second will miss the events until it's subscription and subsequent subscriptions will do the same behaviour.
It is not the missing of events which is my issue. I want all subscribers to get the same data. That is, the stream must start only when the subscription requests are finished.
Is it possible?
Edit: Two approaches and its problems are illustrated below.
1)
public void HotObservableSubscriptionWithRefCount()
{
var obs1 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x;
Console.WriteLine($#"observer1 publishing {publishVal}");
return publishVal;
}).Publish().RefCount();
var obs2 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x + 100;
Console.WriteLine($#"observer2 publishing {publishVal}");
return publishVal;
}).Publish().RefCount();
var sub1 = obs1.Subscribe(x => Console.WriteLine($#"subscriber1 value {x}"));
var sub2 = obs2.Subscribe(x => Console.WriteLine($#"subscriber2
value {x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
var combinedSub = obs1.Merge(obs2).Subscribe(x => Console.WriteLine($#"combined
subscriber value {x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
sub1.Dispose();
sub2.Dispose();
combinedSub.Dispose();
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Problem: The combined subscriber is missing values from two observables because of the delay in subscription
2)
public void HotObservableSubscriptionWithPublish()
{
var obs1 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x;
Console.WriteLine($#"observer1 publishing {publishVal}");
return publishVal;
}).Publish();
var obs2 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x + 100;
Console.WriteLine($#"observer2 publishing {publishVal}");
return publishVal;
}).Publish();
var sub1 = obs1.Subscribe(x => Console.WriteLine($#"subscriber1 value
{x}"));
var sub2 = obs2.Subscribe(x => Console.WriteLine($#"subscriber2 value
{x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
var combinedSub = obs1.Merge(obs2).Subscribe(x =>
Console.WriteLine($#"combined subscriber value {x}"));
obs1.Connect();
obs2.Connect();
Thread.Sleep(TimeSpan.FromSeconds(1));
sub1.Dispose();
sub2.Dispose();
combinedSub.Dispose();
Thread.Sleep(TimeSpan.FromSeconds(1));
}
This will make sure combinedsubsciber will get values in line with any individual subscribers. However even the subscribers are disposed of, the observer still continue provide values.
I need full life cycle control of publisher and subscriber
As pointed out by Felix Keil in a comment, the solution is to use the second approach, and disconnect the connectable observables by disposing the two connections.
var conn1 = obs1.Connect();
var conn2 = obs2.Connect();
/* ... */
conn2.Dispose();
conn1.Dispose();
I have the following code
static void Main(string[] args)
{
//var source = BlockingMethod();
var source2 = NonBlocking();
source2.Subscribe(Console.WriteLine);
//source.Subscribe(Console.WriteLine);
Console.ReadLine();
}
private static IObservable<string> BlockingMethod()
{
var subject = new ReplaySubject<string>();
subject.OnNext("a");
subject.OnNext("b");
subject.OnCompleted();
Thread.Sleep(1000);
return subject;
}
private static IObservable<string> NonBlocking()
{
return Observable.Create<string>(
observable =>
{
observable.OnNext("c");
observable.OnNext("d");
observable.OnCompleted();
//Thread.Sleep(1000);
var source = BlockingMethod();
source.Subscribe(Console.WriteLine);
return Disposable.Create(() => Console.WriteLine("Observer has unsubscribed"));
//or can return an Action like
//return () => Console.WriteLine("Observer has unsubscribed");
});
}
}
which prints
c
d
Observer has unsubscribed
a
b
Can anyone help me get the flow of the control in the program. I did try reading the Call Stack etc..but could not understand everything.
EDIT
Why do i get the above output(which i assume is right) instead of
c
d
a
b
Observer has unsubscribed
The difference in your expected behaviour and the actual behaviour comes from the following line:
var subject = new ReplaySubject<string>();
By default a ReplaySubject uses the Scheduler.CurrentThread. It's as if you declared it like so:
var subject = new ReplaySubject<string>(Scheduler.CurrentThread);
When scheduling using the current thread you get your actions queued up - waiting for the currently executing code to complete before it starts. If you want the code to run immediately you need to use Scheduler.Immediate like so:
var subject = new ReplaySubject<string>(Scheduler.Immediate);
Does this explain it sufficiently?
I'm trying to solve the following:
a) subscriber receives events from IObservable for some time. Then it unsubscribes, do some stuff and then subscribe again. Here it should start receiving events from exactly the same point where unsubscription was performed.
b) Such behavior is desirable for multiple subscribers model. E.g. when one has unsubscribed, others should continue receiving events.
Are there any suggestions from the RX side?
Thanks in advance!
Here's a reasonably simple Rx way to do what you want copied from my answer to this other question. I've created an extension method called Pausable that takes a source observable and a second observable of boolean that pauses or resumes the observable.
public static IObservable<T> Pausable<T>(
this IObservable<T> source,
IObservable<bool> pauser)
{
return Observable.Create<T>(o =>
{
var paused = new SerialDisposable();
var subscription = Observable.Publish(source, ps =>
{
var values = new ReplaySubject<T>();
Func<bool, IObservable<T>> switcher = b =>
{
if (b)
{
values.Dispose();
values = new ReplaySubject<T>();
paused.Disposable = ps.Subscribe(values);
return Observable.Empty<T>();
}
else
{
return values.Concat(ps);
}
};
return pauser.StartWith(false).DistinctUntilChanged()
.Select(p => switcher(p))
.Switch();
}).Subscribe(o);
return new CompositeDisposable(subscription, paused);
});
}
It can be used like this:
var xs = Observable.Generate(
0,
x => x < 100,
x => x + 1,
x => x,
x => TimeSpan.FromSeconds(0.1));
var bs = new Subject<bool>();
var pxs = xs.Pausable(bs);
pxs.Subscribe(x => { /* Do stuff */ });
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
It sounds like you need a "pausable" stream. Assuming that only 1 subscriber will handle the values at a time (while the other subscribers just wait), this solution is probably what you need.