Seems like the best answer is to use Observable.CreateWithDisposable()
e.g.
public IObservable<XElement> GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from completed in Observable.CreateWithDisposable<GetReportDataCompletedEventArgs>(observer =>
{
var subscription = Observable.FromEvent<GetReportDataCompletedEventArgs>(client, "GetReportDataCompleted")
.Take(1)
.Select(e => e.EventArgs)
.Subscribe(observer);
client.GetReportDataAsync(reportName);
return subscription;
})
from close in this.CloseClient(client)
select completed.Result;
}
To make this easier to work with I refactored the CreateWithDisposable into a common function that can be used with all my web service calls, including automatically determining the event name from the event args type:
private IObservable<T> CallService<T>(ICommunicationObject serviceClient, Action start) where T : AsyncCompletedEventArgs
{
if (typeof(T) == typeof(AsyncCompletedEventArgs))
{
throw new InvalidOperationException("Event arguments type cannot be used to determine event name, use event name overload instead.");
}
string completedEventName = typeof(T).Name.TrimEnd("EventArgs");
return CallService<T>(serviceClient, start, completedEventName);
}
private IObservable<T> CallService<T>(ICommunicationObject serviceClient, Action start, string completedEventName) where T : AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable<T>(observer =>
{
var subscription = Observable.FromEvent<T>(serviceClient, completedEventName).Take(1).Select(e => e.EventArgs).Subscribe(observer);
start();
return subscription;
});
}
// Example usage:
public IObservable<XElement> GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from completed in this.CallService<GetReportDataCompletedEventArgs>(client, () => client.GetReportDataAsync(reportName))
from close in this.CloseClient(client)
select completed.Result;
}
Hope this helps someone else!