I'm currently building a wizard system for an application, and we're using ReactiveUI and as a result Rx.
Each step in the wizard implements IWizardStep<T> where T is just the data type that the wizard ultimately produces.
Each step has the capability of surfacing which step should be available for the user to move to next, so as to enable branching based on data entry.
The step can be thought of having a similar structure to:
public interface IWizardStep<T>
{
IObservable<IStepChoice<T>> NextStepChoice {get;}
}
With IStepChoice<T> simply being:
public interface IStepChoice<T>
{
IWizardStep<T> Step {get;}
string Reason {get;}
}
In order to calculate the current path from the start to the end, for display to the user, I need to be able to go from the starting step, and walk along the NextStepChoice chain recursively until it hits a null (it's valid behaviour for the NextStepChoice observable to emit a null to indicate the end of the wizard).
I've taken a look at Observable.Scan but can't for the life of me work out how to get this working properly recursively.
I've also taken a look at Observable.Generate which looks promising, as this is a classic unfold-esque problem; the only issue is that Generate takes a function to determine when to break the loop, but I need to evaluate the inner observable to work this out.
Observable.Generate(
new WizardStepChoice<T>(start, null),
choice => choice != null,
choice => choice.ChosenStep.NextStepChoice,
choice => choice);
This would be ideal, and produce the output I'm after, but the NextStepChoice selector there obviously doesn't compile because it's an IObservable<IWizardStepChoice<T>> rather than an IWizardStepChoice<T>.
I've looked at using Aggregate and Scan but as these are more fold-driven operations, and I've only got the starting element, it's an unfold I'm looking for ala Generate, but I need it to be able to evaluate the nested observable.
Would Observable.Create perhaps be something I could utilise? I've tried it and come up with:
Path = CurrentStep.Select(_ => Observable.Create<IWizardStep<T>>(async observer =>
{
IWizardStepChoice<T> next = new WizardStepChoice<T>(start, null);
observer.OnNext(next.ChosenStep);
while (next != null)
{
next = await next.ChosenStep.NextStepChoice;
observer.OnNext(next.ChosenStep);
}
observer.OnCompleted();
return Disposable.Empty;
}).Aggregate(new List<IWizardStep<T>>(),
(l, s) =>
{
l.Add(s);
return l;
})).Switch().Publish().RefCount();
Which has all the right signature I want IWizardStep<T>->IReadOnlyList<IWizardStep<T>>, so at first glance it looks right, but it doesn't work; it fires, and I can step through, but it hangs once it hits the await and doesn't come back.
I've got a feeling I'm close, and this is a scheduling issue, so my question really is this:
What is the best approach to solve this, am I close?
If this is right, why is there an issue with the await, and how might I solve it?
Update
After a little bit of tinkering I noticed that the await was likely hanging as that observable hadn't yet (and wasn't going to) emit a value (duh), which I've now resolved by initialising each step with a value at the beginning of the wizard.
I've even sanity-checked this by adding a property to IWizardStep<T> - IWizardStepChoice<T> LatestStepChoice {get;} which is just hooked up with:
NextStepChoice.Subscribe(c => _latestStepChoice = c);
This is done on the step class itself, and I can confirm it's working just fine.
Yet still the await hangs, so I tried:
Making it Replay(1) so the await calling .Subscribe() would get the value - this didn't work
Making it Repeat() so even if something is subscribed it'll see the new value - this just made the whole thing hang.
Clearly I'm missing something here, I want it so that when the await queries the observable, it will be given the latest value seen, which is what I thought Replay(1) would achieve; I've also tried PublishLast() but then future updates don't get honoured because of the AsyncSubject<T> behaviour.
For now I've switched to using the self-subscribed property, but it's not ideal, I'd rather not have to break out of querying the observables if I can help it, it feels "hacky".
A recursive walk can transform the tree of observables into a single observable:
static IObservable<IWizardStep<T>> Walk<T>(IWizardStep<T> step)
{
if (step?.NextStepChoice == null)
return Observable.Return(step);
return step.NextStepChoice.SelectMany(choice => Walk(choice.Step)).StartWith(step);
}
Usage:
var steps = await Walk(step).ToArray();
Related
I'm getting a bit of a crash course in ReactiveUI and System.Reactive.Linq after finding that a UI library I need uses it for everything. It seems to be pretty understandable for the most part, but there's one operation that isn't doing anything.
I have a control whose value I need to use in two places. I have an IObservable<T> representing its value, which I'm using as follows:
case 1: I need to feed a value to another observable, by combining this with another observable value. So I produce it using
Observable.CombineLatest(myObservable, otherObservable, (m, o) => ProduceValue(m, o)) This updates exactly as expected. From this, I know that myObservable is firing updates correctly.
case 2:
I need to use this value elsewhere, in a non-observable context. So: myObservable.Do(v => UpdateViewModelWith(v)). This never fires. I've verified that by putting a breakpoint in the lambda and running it under the debugger.
From case 1 I know that the observable is firing correctly. As I understand it, observables are conceptually a lot like Events, (with a bunch of machinery to make them feel more like IEnumerables,) and like Events are perfectly capable of accepting multiple listeners, so the fact that there's two of them shouldn't be a problem. (Verified by changing the order in which the two listeners are set up, which produces no change in observed behavior.) So what can cause case 2 to never run?
#Erwin's answer is close. Just to elaborate:
Do, like most Rx-related functions, is an operator. Operators do nothing without subscriptions. For example:
var source = Observable.Range(0, 5);
var squares = source.Select(i => i * i);
var logged = squares.Do(i => Console.WriteLine($"Logged Do: {i}));
var sameThingChained = Observable.Range(0, 5)
.Select(i => i * i)
.Do(i => Console.WriteLine($"Chained Do: {i}));
//until here, we're in no-op land.
Range, Select and Do are all operators, which do nothing without a subscription. If you want any of this to do anything, you need a subscription.
var subscription = logged.Subscribe(i => Console.Writeline($"Subscribe: {i}");
Output:
Logged Do: 0
Subscribe: 0
Logged Do: 1
Subscribe: 1
Logged Do: 4
Subscribe: 4
Logged Do: 9
Subscribe: 9
Logged Do: 16
Subscribe: 16
Generally, side-effect code (non-functional code) should reside in a Subscribe function. Do is best used for logging/debugging. So if I wanted to log the original, non-square integer, I could do as follows.
var chainedSub = Observable.Range(0, 5)
.Do(i => Console.WriteLine($"Original int: {i}"));
.Select(i => i * i)
.Subscribe(i => Console.Writeline($"Subscribe: {i}");
In pure Rx.NET (without ReactiveUI), there's only one way to get a subscription: the various Subscribe overloads. However, ReactiveUI does have a bunch of functions that create subscriptions themselves so you don't have to deal with them (like ToProperty). If you're using ReactiveUI, those are probably a better choice than Subscribe.
I would guess that you are using an observable without any subscribers, which means it never fires and Do is never activated.
The function that you want is likely IObservable<T>.Subscribe<T>(Action<T> action):
myObservable.Subscribe(v => UpdateViewModelWith(v))
The difference between Do and Subscribe is that Do is a side effect. It's something that happens on the side of the pipeline: "pass me some values, oh and, by the way, do this on the side." But if there is no pipeline, the values are never sent.
On the other hand Subscribe registers an observer. It actually creates a pipeline, by telling the observable: "hey someone is watching, send stuff!"
To better understand what it means, you can also look at the return types:
Do returns an IObservable<T>, since the observable is not consumed.
Subscribe returns an IDisposable, since it registers an observer.
Important: make sure to include using System, otherwise you will only see IObservable<T>.Subscribe<T>(IObserver<T> observer).
By the way, I also got confused regarding Do and Subscribe just this morning and received help on the very nice Reactive Slack. I highly recommend joining if you're working with Rx or ReactiveUI!
Is there maybe something like a "when" statement in C#?
The reason I want this is because an if statement only checks once if a certain property is true at a particular time, but I want it to wait until the property is true.
Anybody know something I can do in C# that would be similar to a "when" statement?
What you want is SpinWait
e.g. SpinWait.SpinUntil(() => condition);
It will sit there until it either times out (with your specified timeout) or the condition is met.
There is no when control statement, but there are two options which might meet your needs:
You can use a while(predicate){} loop to keep looping until a condition is met. The predicate can be any expression which returns true/false - as long as the condition is true, it will loop. If you just want to wait without consuming too much CPU, you can Sleep() the thread within the loop:
while(name == "Sebastian")
{
// Code to execute
System.Threading.Thread.Sleep(1000);
}
If you property is a numeric range, you could use a for loop, but that doesn't sound like what you want.
If you want to deal with an asynchronous world than you may be should look at the library Rx.NET. Let's look at the simple example: suppose you want to read strings from the console and when user inputs word "hello" you need to print "world" in the response. This simple example can be implemented as follows:
var inputLines = new Subject<string>();
inputLines.Subscribe(info =>
{
if (info == "hello")
Console.Out.WriteLine("world");
});
while (true)
{
var line = Console.In.ReadLine();
inputLines.OnNext(line);
}
So, there are explicit when action, that we pass in the Subscribe(...) function.
In this simple example usage of Rx.NET is obviously unnecessarily and you shouldn't do it. But in more complex scenarios this is a very helpful library. You can see, that with Reactive Extensions you split the logic of your application from the main event-pool, where you can want to do some other work, not related to the application logic. Also, there is high flexibility that you can get with this library, because it's very dynamic - you can subscribe and unsubscribe different events in run-time at any time.
You can notice, that there is another way to solve my example in the event-based paradigm. We can simply use built-in events like this:
public static event EventHandler<string> InputEvent;
public void Run()
{
InputEvent += (sender, line) => {
if (line == "hello")
Console.WriteLine("world");
};
while (true) {
var line = Console.In.ReadLine();
InputEvent?.Invoke(this, line);
}
}
And this is a right point, sometimes you can replace Reactive Extensions with simple events because they are connected. But when you need to build a complex pipeline from many event sources and using many different tightly coupled actions, then Reactive Extensions allow you to nicely build this pipeline in the very declarative way.
You can use 'async' and 'await' to wait unit a certain 'Task' is complete. 'await' is somewhat similar to the 'when' statement you need. Only it pauses the current 'Task' until the awaited 'Task' finished with any result not just when an expression becomes 'true'. See also TaskCompletionSource.
What are you trying to achieve here? Are you running a synchronous process or are you waiting for something asynchronous to happen?
If you're synchronous then while is probably the correct solution:
var result = 0;
while(result != 6)
{
result = RollADie();
Console.WriteLine($"I rolled a {result}");
}
Console.WriteLine("At last, a six!");
But - if you're waiting for something asynchronous to happen then a different solution is called for. An asynchronous scenario is where you want your code to 'hang around and wait, doing nothing' until the condition is fulfilled.
In that case the modern C# solution is asynchronous programming using the async and await keywords, along with the Task class (and it's generic cousin Task<TResult>). That's probably a bit deep to go into here, but here's a pretty good primer.
What's important is that you don't use a solution based on while in order to deal with asynchronous processes. You'll just send the CPU spinning in circles, chasing it's own tail so to speak, when really you want to say "now stop working on this until X happens". Also, avoid any solution based on while combined with Thread.Sleep for related reasons.
As a reaction from Shane Neuville I'm changing my question. But yes. It may be more complicated then I first thought it was.
I have a ReactiveList<ServiceEntryModel> Services; that will be populated when the view have appeared. With every entry in the list I have used the function called Services.ItemsAdded.Subscribe() where the RxCell will be populated with data that at the end of the whole list should be ordered in a specific way. As of now I have have two separate ReactiveCommands.
1.ReactiveCommand<Unit, List<ServiceEntryModel>> RefreshServices;
2.ReactiveCommand<Unit, List<ServiceEntryModel>> OrderByCommand;
The second command should be called when the whole list is populated from the Services.ItemsAdded.Subscribe method. How can I check/know when every entry is done?
I have tried something with counters and ObservablePropertyHelpers, but without any luck so far.
Here is my ViewModel part that should be relevant
Services.ItemsAdded.Subscribe(async entryItem =>
{
ServiceEntityStatusDto serviceEntityStatus = await GetServiceEntryStatus(entryItem);
if (serviceEntityStatus != null)
{
entryItem.AvailabilityStatus = serviceEntityStatus.AvailabilityStatus;
entryItem.PerformanceStatus = serviceEntityStatus.PerformanceStatus;
}
counterServices++;
//await OrderServices.Execute(); //TODO infinite loop ?
});
The part is commented is a infinite loop, because the Services.ItemsAdded will be continue to be called.
The user can pull to refresh and the first command will be called again, so right when that is done. The seconds should order the whole list again.
Is this enough?
RefreshServices
.SelectMany(_ => OrderServices.Execute())
.Subscribe();
Or the InvokeCommand syntax
RefreshServices
.InvokeCommand(OrderServices);
The distinction being that with InvokeCommand OrderServices will not execute if it's already executing which might not fit your use case. For example maybe RefreshServices can execute again while OrderServices is still running.
I just started messing around with reactive programming, and I know just enough to write code but not enough to figure out what's happening when I don't get what I expect. I don't really have a mentor available other than blog posts. I haven't found a very good solution to a situation I'm having, and I'm curious about the right approach.
The problem:
I need to get a Foo, which is partially composed of an array of Bar objects. I fetch the Bar objects from web services. So I represented each web service call as an IObservable from which I expect 0 or 1 elements before completion. I want to make an IObservable that will:
Subscribe to each of the IObservable instances.
Wait for up to a 2 second Timeout.
When either both sequences complete or the timeout happens:
Create an array with any of the Bar objects that were generated (there may be 0.)
Produce the Foo object using that Bar[].
I sort of accomplished this with this bit of code:
public Foo CreateFoo() {
var producer1 = webService.BarGenerator()
.Timeout(TimeSpan.FromSeconds(2), Observable.Empty<Bar>());
var producer2 = // similar to above
var pipe = producer1.Concat(producer2);
Bar[] result = pipe.ToEnumerable().ToArray();
...
}
That doesn't seem right, for a lot of reasons. The most obvious is Concat() will start the sequences serially rather than in parallel, so that's a 4-second timeout. I don't really care that it blocks, it's actually convenient for the architecture I'm working with that it does. I'm fine with this method becoming a generator of IObservable, but there's a few extra caveats here that seem to make that challenging when I try:
I need the final array to put producer1 and producer2's result in that order, if they both produce a result.
I'd like to use a TestScheduler to verify the timeout but haven't succeeded at that yet, I apparently don't understand schedulers at all.
This is, ultimately, a pull model, whatever gets the Foo needs it at a distinct point and there's no value to receiving it 'on the fly'. Maybe this tilts the answer to "Don't use Rx". To be honest, I got stuck enough I switched to a Task-based API. But I want to see how one might approach this with Rx, because I want to learn.
var pipe = producer1
.Merge(producer2)
.Buffer(Observable.Timer(TimeSpan.FromSeconds(2), testScheduler))
.Take(1);
var subscription = pipe
.Select(list => new Foo(list.ToArray())
.Subscribe(foo => {} /* Do whatever you want with your foo here.*/);
Buffer takes all elements emitted during a window (in our case in two seconds), and outputs a list.
If you want to stick with your pull model, instead of a subscription you could do:
var list = await pipe;
var foo = new Foo(list.ToArray());
//....
So I have written a rough draft of the problem I'm encountering. I have an IObserverable< IObservable< TResult>> which contains my stream, and I would like to use switch to get the latest items from it, however the problem I have can be neatly demonstrated with the code below:
var obs = Observable.Create<IObservable<int>>(sub =>
{
var item = Observable.Create<int>(innersub =>
{
var count = 0;
return Observable.Interval(TimeSpan.FromSeconds(2)).Subscribe(x => innersub.OnNext(count++));
}).Publish().RefCount();
return Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(x => sub.OnNext(item));
});
obs.Switch().Subscribe(x => Console.WriteLine(x));
The above test case shows that when used in conjunction with Publish().RefCount() the switch first unsubscribes and then subscribes to the new item.
What I would like is for a continuous stream of numbers going up, however the test shows the "item" is disposed of first before the new subscription hits and I lose that count and have to start again.
If the item is the same, and refcount is used, what I would like is for the subscription to happen first, so refcount is happy, and then the old subscription disposed of. Is this behavior that RX can demonstrate by default or would it require some bodging to get right? I'm confident I could write a simple enough extension method based on a cut down version of the RX source code but if it already exists or there is a better way I'd like to know first.
Edit: The code written was a naive example to demonstrate the problem in a simple way. What I actually have is an observable that publishes a new observable regularly, which has different filters on it, but which ultimately boils down to the same publish/refcount observable at the base of it all. (The where clause changes, or the select does something different. The real use is a .Merge() of several streams so I'm confident in my logic and my conclusion of the problem). I'm well aware that my example could be simplified.
You're going to have to look at the source as the previous observable gets disposed before the current one gets subscribed to. That's how .Switch() works.
If Rx disposed after the new subscription, the intent of your code appears to be the equivalent of simply doing this:
var obs = Observable.Create<int>(innersub =>
{
var count = 0;
return Observable.Interval(TimeSpan.FromSeconds(2))
.Subscribe(x => innersub.OnNext(count++));
});
obs.Subscribe(x => Console.WriteLine(x));
And in this example it then boils down to:
var obs = Observable.Interval(TimeSpan.FromSeconds(2));
obs.Subscribe(x => Console.WriteLine(x));
Perhaps you could let us know what your underlying requirement is and we could work on that?
This operator is designed primarily for cold observables. Switch unsubscribes from the previous observable before subscribing to the new one. Otherwise there would exist a race condition where extra events might slip through during the brief period it was subscribed to both.
Since your underlying observable is hot, you might consider an alternative solution where you just modify the filter/select "on the fly" instead of using Switch to "re-subscribe". Something like:
source
.Where(t => ApplyCurrentFilter(t))
.Select(t => ApplyCurrentProjection(t))
.Subscribe(...);
// do something that changes what `ApplyCurrentFilter` does...
I don't know if this is better or worse than your current solution, but it does avoid the need to unsubscribe/resubscribe from source data.
As has been said, Observable.Create produces a cold Observable, and Publish.RefCount only makes it hot while there are still subscribers there. It would be possible to write your own version of Switch that subscribed the new subscriber before disposing the old. But I'd be very wary of race conditions. And in general, it feels a bit of a weird thing to do, which in Rx usually signals there's another way to do what you want that's cleaner.
In this example, if you have the desired result, there's no purpose to publishing many Observables and Switching them because really you just want to be subscribed to one observable for the full duration. Hence how it boils down to what Enigmativity says.
However, obviously it's a contrived example so let's assume there's a more complicated situation that requires this approach - it may help if you're able to elaborate. From the example, it seems you only want to subscribe to the inner observable once, ever. Based on that requirement, RefCount is not appropriate, but I assume you're using it because you want a shared observable at the core, which you're wrapping with other operators that you want to act differently each time. If that's the case, you could possibly use an approach like this:
var obs = Observable.Create<IObservable<int>>(sub =>
{
var item = Observable.Create<int>(innersub =>
{
var count = 0;
return Observable.Interval(TimeSpan.FromSeconds(2))
.Subscribe(x => innersub.OnNext(count++));
}).Publish();
bool connected = false;
var disposables = new CompositeDisposable();
disposables.Add(Observable.Interval(TimeSpan.FromSeconds(10))
.Subscribe(x =>
{
// push the new stream to the observer first
sub.OnNext(item);
if (!connected)
{
connected = true;
disposables.Add(item.Connect());
}
}));
return disposables;
});
I haven't thought through potential race conditions with this approach, etc, and a lot depends on your real situation. However, in the basic test from the original post, this seems to behave how you want it to.