Simplify asynchronous Silverlight Web service requests with RX

I used Rx to write a simplified Silverlight client library for my WCF web service, but sometimes I noticed that I was missing completed events.

< p>

public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from request in Observable. ToAsync(client.GetReportDataAsync)(reportName)
from result in Observable.FromEvent(client, "GetReportDataCompleted").Take(1)
from close in this.CloseClient(client)
select result.EventArgs.Result;
}

I think the problem is due to the web service being called and returning before subscribing to the completed event. I can’t figure out how to get Rx in Subscribe to the event before the asynchronous call. I tried StartWith, but this requires the input and output types to be the same, any ideas?

It seems that the best answer is to use Observable.CreateWithDisposable()

< p>For example

public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())< br /> from completed in Observable.CreateWithDisposable(observer =>
{
var subscription = Observable.FromEvent(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 use, I refactored CreateWithDisposable into a All web service calls are used together The public functions, including the automatic determination of the event name from the event args type:

private IObservable CallService(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(serviceClient, start, completedEventName) ;
}

private IObservable CallService(ICommunicationObject serviceClient, Action start, string completedEventName) where T: AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable (observer =>
{
var subscription = Observable.FromEvent(serviceClient, completedEventName).Take(1).Select(e => e.EventArgs).Subscribe(observer) ;
start();
return subscrip tion;
});
}

// Example usage:
public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from completed in this.CallService(client, () => client.GetReportDataAsync(reportName))
from close in this.CloseClient (client)
select completed.Result;
}

///
/// Asynchronously closes the web service client
// /

/// The web service client to be closed.
/// Returns a cold observable sequence of a single success Unit.
private IObservable CloseClient(WebServiceClient client)
{
return this.CallService(client, client.CloseAsync, "CloseCompleted");< br />}

Hope this helps others!

I used Rx to write a simplified Silverlight client library for my WCF web service, but sometimes I noticed that I was missing completed events.

< /p>

public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from request in Observable.ToAsync(client.GetReportDataAsync)(reportName)
from result in Observable.FromEvent(client, "GetReportDataCompleted").Take(1)
from close in this. CloseClient(client)
select result.EventArgs.Result;
}

I think the problem is due to the web service being called and returning before the subscription has been completed. I can’t figure it out How can I make Rx subscribe to the event before the asynchronous call. I tried StartWith, but this requires the input and output types to be the same, any ideas?

It seems that the best answer is to use Observable.CreateWithDisposable()

For example

public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from completed in Observable.CreateWithDisposable (observer =>
{
var subscription = Observable.FromEvent(client, "GetReportDataCompleted")
.Take(1)
.Select(e => e. EventArgs)
.Subscribe(observer);
client.GetReportDataAsync(reportName);
return subscription;
})
from close in this.CloseClient(client)< br /> select completed.Result;
}

To make this easier to use, I refactored CreateWithDisposable into a public function that can be used with all my web service calls, including from The event args type automatically determines the event name:

private IOb servable CallService(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(serviceClient, start, completedEventName);
}

private IObservable CallService(ICommunicationObject serviceClient, Action start , string completedEventName) where T: AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable(observer =>
{
var subscription = Observable.FromEvent(serviceClient, completedEventName ).Take(1).Select(e => e.EventArgs).Subscribe(observer);
start();
return subscription;
});
}< br />
// Example usage:
public IObservable GetReport(string reportName)
{
return from client in Observable.Return(new WebServiceClient())
from completed in this.CallService(client, () => client.GetReportDataAsync(reportName))
from close in this.CloseClient(client)
select completed.Result;
}

/// < summary>
/// Asynchronously closes the web service client
///
/// The web service client to be closed.< /param>
/// Returns a cold observable sequence of a single success Unit.
private IObservable CloseClient(WebServiceClient client)
{
return this.CallService(client, client.CloseAsync, "CloseCompleted");
}

Hope this helps others!

Leave a Comment

Your email address will not be published.