I have to say I've got some problems with this. First it took me a horrible long time to realise that Subject can be used as an Observable-Source you can publish values to. And even worse is the way I have to use the Schedule/OnNext - mess.

Don't know if there is any better way, but why was the way suggested by the video droped?

But boths of theses seems to me like "breaking the pattern" - if we use a concrete scheduler in the definition of the Observable-Source then what about SubscribeOn (the one with the IScheduler overload)?

What I had expected was something like Create with "Action<ISubscriber>" or something like

public static IObservable<tData> ToObservable<tData>(this IEnumerable<Tuple<DateTimeOffset, tData>> source)
{
/* feed the data (snd) into a IObsevable and use the fst component for the scheduler, whatever it might be */
}

It runs and should do as the method explanations indicate but doesn't delay each quote as expected (unless I don't use the scheduler?), but I shouldn't think you need to for each over it like the other attempts above...

Hi - you never have to "foreach" - you can allways use some LINQ-syntax to do the same (and tools like ReSharper even have some automatic code-conversation between the two ways) - it's just a matter of taste and the way the code might look if you use Select/Aggregate/whatever to make it LINQish.

IObservable<StockQuote> GetQuotes(IScheduler scheduler, IEnumerable<StockQuote> quotes)
{
// TODO: Create an observable source of stock quotes
// HINT: Use both the scheduler and the quotes and think about how to create sources which are like events
return quotes.ToObservable<StockQuote>().ObserveOn(scheduler);
}
IObservable<object> Query(IObservable<StockQuote> quotes)
{
// TODO: Write a query to grab the Microsoft "MSFT" stock quotes and output the closing price
// HINT: Make sure you include a property in the result which has a type of DateTime
var filteredQuotes = from quote in quotes
where quote.Symbol == "MSFT"
select quote;
return filteredQuotes;
}

look at the implementation of MyHistorcalScheduler.Run(). ToObservable() will schedule all StockQuotes right away with a DueTime of 0 (because you ignore the StockQuote.Data value completely). AdvanceTo() then throws.

I am not sure what is the right implementation of GetQuotes(). I am guessing that

when called it should not block but return right away (with the IObservable<StockQuote>), regardless whether iterating over the sequence of stock quotes blocks or not

when called several times it should produce the sequence serveral times from the beginning

it should call OnNext of the observer at StockQuote.Date measured in the clock time of the passed scheduler

one call of OnNext should be finished before calling the next time (might conflict with the point obove)

But although CbrAlnK advocates avoiding foreach, doing something more "LINQish", the problem is that this is really just a foreach in disguise. I think the foreach-based solutions are arguably better, because they reveal what's really happening.

For example, one unfortunate aspect of this whole lab is that it requires the entire schedule to be loaded up before you Run the scheduler. I don't think it's entirely obvious, but a) that code snippet above evaluates the callback (the one that calls scheduler.Schedule(...)) once for every quote inside the call to Subscribe, and b) the example only works because of that synchronous execution of the quotes observable. In other words, this only works because someEnumerable.ToObservable().Subscribe is just an obscure way of writing a foreach loop.

I was hoping to be able to do something that felt a bit more in keeping with Rx. What I wanted was some way to iterate over a sequence (either in IEnumerable<T> or IObservable<T> form) that was able to select a scheduling time out of that sequence as it generated an observable. So I wanted to be able to write this sort of thing:

return FeedIntoScheduler(quotes, scheduler, q => q.Date);

The theory there being that I could take an IEnumerable<T> and convert it into an IObservable<T> via a scheduler, where the schedule timings were provided by the Date property of the stock quote. I figured this might be waht Observable.Generate was for, and it sort of it:

But there's a problem here. The actual work of kicking off the observable is done via the scheduler you pass, and it will pick whatever time the scheduler claims "Now" is. And it turns out that this will crash the historical scheduler provided in the challenge - it's incapable of handling "now" work items. You can fix this by changing the call to AdvanceTo to this:

if (dt > Now)
{
AdvanceTo(dt);
}
else
{
next.Invoke();
}

But then a new problem emerges: it turns out the data is in reverse chronological order. So the first item that this schedules is the most recent one, causing the virtual clock to run right to the end, messing things up for the remaining items

In conjunction with the FeedIntoScheduler method and the fix to MyHistoricalScheduler, this appears to work. It feels in keeping with the sort of shape I thought I was supposed to be getting for the solution - my GetQuotes builds an IObservable<T> from an IEnumerable<T>, feeding it into a scheduler according to a clearly-defined schedule.

However, this can't be the "right" solution. For one thing, I had to fix the scheduler to deal with a scenario it wasn't built to deal with. Moreover, this is now very much more complex than the foreach versions originall submitted, and with very little discernable benefit.

About the one advantage I could think of for this is was that it might work with an infinite sequence - the observable generator is working lazily whereas the foreach versions iterate through the entire thing before even starting the scheduler. For that to work, the enumerable source needs to produce items in order, because the "orderby" query in GetQuotes has to go - orderby can only work on a finite sequence.

I modified the quote source to feed the items out of the file in asecending date order, and then to just make up an infinite stream of quotes thereafter. And that does actually appear to work. So perhaps this is a scenario for which this much more complex approach is "right".

I think rab36's solution is in much the same spirit, but using Create instead of Generate. I see that the Create-based solution also ends up requiring the input to be sorted to work correctly.

Remove this comment

Remove this thread

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums, or
Contact Us and let us know.