I have a RIA Services data service that has several function calls that look like this:
public InvokeOperation<T> SomeFunc(
SomeData data,
Action<InvokeOperation<T>> callback,
object userState)
How would I use this with Reactive Extensions so I can subscribe to the callback and get the InvokeOperation result?
Update: here is my current implementation of Enigmativity’s hybrid solution. I needed the actual InvokeOperation not just the value since the InvokeOperation UserState can be valuable. It should be noted that I haven’t tested error handling at all.
public static class ObservableEx
{
public static IObservable<InvokeOperation<T>> ObservableInvokeOperation<T, Tdat> (
Func<Tdat, Action<InvokeOperation<T>>, object, InvokeOperation<T>> func,
Tdat data,
System.Reactive.Concurrency.IScheduler scheduler )
{
return
Observable.Defer<InvokeOperation<T>>( () =>
FromCallbackPattern<Tdat, T>( func, scheduler )
.Invoke( data ) );
}
private static Func<P, IObservable<InvokeOperation<T>>> FromCallbackPattern<P, T> (
Func<P, Action<InvokeOperation<T>>, object, InvokeOperation<T>> call,
IScheduler scheduler )
{
return p =>
{
var subject = new AsyncSubject<InvokeOperation<T>>();
try
{
call( p, iot =>
{
if ( iot.HasError )
{
subject.OnError( iot.Error );
}
else
{
subject.OnNext( iot );
subject.OnCompleted();
}
}, p );
}
catch ( Exception ex )
{
subject.OnError( ex );
}
return subject.ObserveOn( scheduler );
};
}
}
useage given function
public InvokeOperation<int> SomeFunc(SomeData data, Action<InvokeOperation<int>> callback, object userState)
var myobs = ObservableEx.ObservableInvokeOperation<int, SomeData>( myRIAContext.SomeFunc, data, Scheduler.ThreadPool );
This works great for any function that with matches the given function signature. Unfortunately now I’ve run into some variations such as
Func<T1, Action<InvokeOperation<T>>, object>
Func<T1, T2, Action<InvokeOperation<T>>, object>
Anyone have any suggestions to convert this to be able to handle any InvokeOperation method I want to throw at it?
EDIT1: See below for a hybrid solution based on Paul Betts’ answer and mine.
EDIT2: See below for a “third-generation” solution based on the OP’s update.
The callback is a little difficult to deal with and I must say that turning this into an observable is a good way to go.
I’ve got an approach that worked for me.
The basic approach is to turn the
SomeFuncoperation into aFunc<T>and then callObservable.Starton that. I’ve wrapped this inObservable.Createto keep it clean and I’ve added error handling. I’ve done basic testing, but nothing too robust.Consuming the code looks like this:
I’ve assumed that your RIA service class is
RiaService<T>and built theSomeObservableFuncextension method like this:Yell out if this works for you.
EDIT1
I liked Paul Betts’ solution because it didn’t use
ManualResetEvent, but it didn’t compile and also didn’t handle internal errors that could occur during the RIA service call, so I’ve created the follow hybrid solution.My extension method now looks like this:
And this uses the reworked
FromCallbackPatternoriginally created by Paul Betts:It works against my test code and I think this is a nicer overall solution.
EDIT2
This version of the solution is designed to allow differing number of parameters plus the user state to be passed to the
FromCallbackPatternextension methods.I started with this general purpose
FromCallbackPatternextension method:Then I needed a series of private
Reduceextension methods to reduce the various service calls down toAction<Action<InvokeOperation<T>>>delegates:Now I can write the plain
FromCallbackPatternextension methods:And then, finally, the original
SomeObservableFunc/ObservableInvokeOperationextension methods (now also renamed toFromCallbackPattern):Obviously you need to replace the references to
RiaService<T>with your RIA service class type.These methods can be called like this:
Phew! How’s that now?