Monday, July 16, 2012

Reactive Extensions – Simple asynchronous repository

In Silverlight, all webservices calls are asynchronous. Therefore, when implementing a repository in Silverlight we have to do things a little bit differently as we would have done in Asp.Net or WPF.

Let’s take an example. We have a website exposing a list of customers through a WCF service. We want our Silverlight application to list all these customers inside a ListBox. The service can return tenth of thousands of customers. Because of that we cannot retrieve all of them within a single call.

Let’s see the definition of the Service :

[ServiceContract(Name = "CustomerService")]
public interface ICustomerService {
    [OperationContract]
    int Count();
 
    [OperationContract]
    IEnumerable<Customer> Get(int start, int count);
}

...
...

public class CustomerReactiveRepository {
    public IObservable<Customer> GetAll()
    {
        return Observable.Create<Customer>(observer => OnSubscribe(observer));
    }
 
    private static Action OnSubscribe(IObserver<Customer> observer)
    {
        try {
            var client = new CustomerServiceClient();
            client.CountCompleted += (sender, e) =>
            {
                if (e.Result > 1000)
                {
                    var state = new GetState { Count = e.Result, Offset = 0, Step = 500 };
                    ((CustomerServiceClient)sender).GetAsync(state.Offset, state.Step, state);
                }
                else ((CustomerServiceClient)sender).GetAsync(0, e.Result);
            };
 
            client.GetCompleted += (sender, e) =>
            {
                foreach (var c in e.Result)
                    observer.OnNext(c);
 
                var state = e.UserState as GetState;
 
                if (state != null && state.Offset + state.Step < state.Count)
                {
                    state.Offset += state.Step;
                    ((CustomerServiceClient)sender).GetAsync(state.Offset, state.Step,
                                                                state);
                }
                else {
                    ((CustomerServiceClient)sender).CloseAsync();
                    observer.OnCompleted();
                }
            };
 
            client.CountAsync();
        }
        catch (Exception e)
        {
            observer.OnError(e);
        }
 
        return () => { };
    }
 
    private class GetState {
        public int Offset { get; set; }
        public int Step { get; set; }
        public int Count { get; set; }
    }
}

Posted via email from Jasper-Net