Using Rx to simplify an asynchronous Silverlight web service request
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Silverlight-era async code often relied on callback-heavy event patterns generated by service proxies. That worked, but it quickly became hard to compose, cancel, or test once multiple requests and UI updates were involved. Reactive Extensions, usually called Rx, improved this by turning event-based operations into composable observable streams.
The Problem with Event-Based Service Calls
Older Silverlight service clients often exposed methods like GetOrdersAsync() and paired them with completion events like GetOrdersCompleted. That model spreads control flow across separate methods and encourages flag-heavy UI code.
A typical event-based version looked like this:
That is manageable for one request. It gets harder when you need retries, throttling, chaining, or cancellation.
Wrapping the Async Pattern with Rx
Rx lets you convert event-based async calls into IObservable<T>. Once the service call is an observable, you can compose it with other streams and keep the logic declarative.
One common approach is to wrap the completion event manually:
Then you can subscribe to the result:
The important shift is that the request is now a value-producing stream, not just a callback hook.
Composing Multiple Async Steps
Rx becomes more valuable when the workflow has multiple async stages. For example, suppose you first fetch a customer and then use that customer id to fetch orders.
Without Rx, this usually becomes nested event handlers. With Rx, the flow stays linear and readable.
Cancellation and Lifetime Management
One practical benefit of Rx is that subscriptions are explicit. You can keep the returned IDisposable and dispose it when the view is unloaded.
In UI-heavy Silverlight code, this helps avoid stale callbacks updating controls after the view is gone.
Error Handling Becomes Centralized
With raw event handlers, each callback usually needs its own error path. Rx gives you one error channel through Subscribe.
You can also add retry or timeout behavior in one place:
This is the kind of policy logic that gets messy fast in plain event code.
Why This Was Useful in Silverlight
Silverlight apps were UI-driven and network-heavy, but they predated the modern async and await style that later made asynchronous .NET code much easier. Rx filled that gap well because it:
- reduced callback nesting
- made UI-thread marshaling explicit with
ObserveOnDispatcher - gave one place for retry, timeout, and error handling
- made multi-request workflows easier to compose
Today, in newer .NET UI stacks, async and await are usually the first choice. But for Silverlight or older event-based APIs, Rx was and still is a strong simplification tool.
Common Pitfalls
- Subscribing to an event stream without unsubscribing or disposing the subscription.
- Forgetting
ObserveOnDispatcher()before updating UI controls. - Wrapping the event but still keeping most business logic in nested callbacks.
- Using Rx for one trivial request without benefiting from composition.
- Assuming Rx cancels the underlying service operation automatically unless the underlying API supports that behavior.
Summary
- Rx helps turn Silverlight service callback patterns into composable observable sequences.
- This removes a lot of event-handler nesting and scattered control flow.
- '
ObserveOnDispatcher()is important when the subscription updates UI state.' - Rx becomes especially valuable when chaining requests, handling errors, or applying retry and timeout rules.
- In legacy Silverlight code, Rx was often the cleanest bridge from event-based async code to something more maintainable.

