Reputation: 1499
I want to write a library that would communicate with a web server and expose data from it to the rest of the world. The web server is nothing special, it exposes several REST methods, mostly GET and POST.
Since I am relatively new to Reactive Extensions (but I love it already) I ask for advice. I decided that the interfaces of the library would expose IObservables. But I dont know how exactly to implement this. I'm thinking I have several options:
1) Expose IObservable<IEnumerable<T>>
. Makes sense, REST service returns all requested data at once. User calls Subscribe(), only one IEnumerable is pushed, OnDone is called. So Subscribe() would need to be called multiple times.
2) Expose IObservable<T>
. Could be a good choice in some cases I guess. Subscribe() would only be called once, to get other data, there would be methods Refresh() or NextPage() (...) to get more data to the stream. (then instead of IObservable<T> GetResource...
it could be a property, IObservable<T> Resource { get; }
3) Forget Rx, do it old fashioned way via events (worst possible thing IMO)
4) Some other way?
Anyone with experience in this area? What I am concerned about is Refreshing (asking for new data), Paging, combining the results and generally having a good maintainable design.
Thx for any advice
Upvotes: 3
Views: 1536
Reputation: 117154
I would suggest using the following interface:
public interface IRestService
{
IObservable<T> GetResources<T>();
}
There are a number of reasons behind this choice.
To expose an IObservable<IEnumerable<T>>
mixes reactives with interactives (or observables with enumerables) and would force your queries to call .ToObservable()
or, even worse, .ToEnumerable()
to construct a basic SelectMany
query. It's better for you to better to keep your queries and subscribing code nice an clean.
Now, you suggested that with an IObservable<T>
you would only subscribe once and you would require a Refresh
or NextPage
call to get more data to the stream. That's not a good idea. You should instead think of a single subscription would return all the results of a single REST call and then call OnComplete
. If you want to invoke a new REST call then just subscribe again.
Further, the semantics of a single subscription call are not clearly expressed in the code. So you need to think about maintaining your code. When you look at the code in the future what are you likely to think that the semantics are? I would suggest that the semantics of a a single subscription would map to a single REST call would be more likely. Your code would have the potential of being more confusing otherwise.
Even further, you should avoid a single subscription model because if any exception is thrown then your observable is done and, of course, calling web services can be very error prone. If you have an error with a multiple subscription model you can recover more easily.
I would also avoid IObservable<T> Resources { get; }
because it suggests some sort of "fixed" value where instead it is more dynamic - in other words each call may give you different values. It is better to call a GetResources
method rather than a Resources
property.
Some, bottom-line, I'd have a IObservable<T>
that abstracts a single call to your underlying REST service.
Upvotes: 2
Reputation: 74560
You're combining two concerns here which really should be addressed separately.
The first is your code going and getting data from other sources. The second is publishing that data to interested parties when there is new data available.
In regards to the first, the Reactive Extensions aren't going to help. Your concern here is to get the data on a timed interval; it has to be a timed interval because when making calls to REST services in your code, there's no callback, there's nothing for you to hook into that the service can call.
If there was some sort of callback into your code from the external service, then that could be wrapped in some IObservable<T>
implementation which you could then subscribe to and perform your operations on (just forwarding the subscription, really).
The only way you could use the Reactive Extensions for the first concern is to set off a timer by using the static Timer
method on the Observable
class.
For the second concern (after you have your data and you want to notify subscribers), you absolutely can and should use an IObservable<T>
implementation to notify subscribers.
In this case, I strongly recommend that you don't try and deviate from the intent of the Subscribe
method on the IObservable<T>
interface. You should expose a method that will give you the IObservable<T>
that anyone can subscribe to (whether or not the IObservable<T>
is hot or cold before Subscribe
is called is up to you) and unsubscribe from by calling the Dispose
method on the IDisposable
interface implementation returned from the call to Subscribe
.
That way, your clients can get the IObservable<T>
, subscribe for the notifications they want, and then unsubscribe when they're done.
Upvotes: 0