Using reactive extensions for a push-based client-server communication with Silverlight and WCF

8 minutes read

In the previous article, I explained you how Rx can improve the process of downloading data from a WCF service. This was a classical scenario where the Silverlight client explicitly asks for data from the server.

Now we will see how we can use Rx in a push based scenario where the server sends data to the Silverlight client without any explicit demand from it.

The prerequisites

In order to follow this article you need to make sure you have the latest version of Rx and the Silverlight SDK. At the time theses lines were written I was using Silverlight 4.0.60310.0 and Rx 1.0.10621.0.

The context

We want to create a Silverlight application and a WCF service that pushes temperature data in the Celsius scale to the client at random intervals. The Silverlight application (aka client) has two independent modules, one for displaying the temperature received in the Celsius scale and the other one to display it in the Fahrenheit scale. Even though we have two independent modules, we want them to use the same connection to the WCF service to receive the temperature. The module will also have no direct reference on the WCF service proxy allowing us to use dependency injection and unit testing of the modules’ view models.

Duplex service

A duplex service is a service where both the client and the server can send data through the same connection which is exactly what we want here. We chose to create a service using the Subscribe-Publish pattern. The client will create a connection to the server using a Subscribe method and then wait asynchronously for data from it.

In WCF, this kind of scenario is made possible by using a service callback. This callback is an interface defined in the server side and implemented at the client’s side which the server will use whenever it wants to send data to the client.

In order to use these callbacks we need to choose a compatible WCF binding. When creating a WPF application we can use all the bindings provided by default such as wsDualHttpBinding but in Silverlight we are somewhat restricted. In Silverlight we can use netTcpBinding or pollingDuplexHttpBinding.

The first one is a little bit harder to configure than the second one and I will write about it in another post. For today, I’ll focus on the second one.

PollingHttpDuplexBinding

This binding is a little bit special because it’s a duplex binding over an http communication. As it is well-known, http is not a duplex communication protocol. In http the client has to explicitly ask the server for data. When we need to have such a duplex communication using Silverlight we can implement a custom polling code. Polling is the process where the client regularly asks the server whether some new data is available or not. The pollingHttpDuplexBinding already implements the polling code for us directly inside the communication layer improving the performance of the overall process.

The pollingHttpDuplexBinding is not included by default in Silverlight or WCF. We can find the assemblies for the client and the server in the Silverlight SDK. On my hard drive theses dlls are located in the C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries folder so for you it should be there C:[ProgramFilesArchitecture]\Microsoft SDKs\Silverlight[SilverlightVersion]\Libraries.

There is two assemblies required for this binding to work, one for the client and the other one for the server.

In the web project hosting the Silverlight application we add a reference to the server’s System.ServiceModel.PollingDuplex.dll and in the Silverlight client we add a reference to client’s System.ServiceModel.PollingDuplex.dll.

Creating the service

In the web project we first create two interfaces defining the WCF contracts for both the service and it’s service callback.

[ServiceContract(CallbackContract = typeof(ITemperatureServiceCallback))]
public interface ITemperatureService {
    [OperationContract(IsOneWay = true)]
    void Subscribe();

    [OperationContract(IsOneWay = true)]
    void Unsubscribe();
}
public interface ITemperatureServiceCallback {
    [OperationContract(IsOneWay = true)]
    void PushTemperature(double temperature);
}

Then we can create an implementation of the service (this implementation is a simple one that would not suit for production needs). This implementation creates a timer that will call the service callback of each clients connected to the services to send new random temperature data.

public class TemperatureService : ITemperatureService {
    private static readonly object _locker = new object();
    private static readonly List<OperationContext> _clients = new List<OperationContext>();
    private static readonly Random _random = new Random();

    private static Timer _updateTimer;

    static TemperatureService()
    {
        _updateTimer = null;
    }

    public void Subscribe()
    {
        if (_clients.Contains(OperationContext.Current) == false)
        {
            lock (_locker)
            {
                if (_clients.Count == 0)
                    _updateTimer = new Timer(TimerTick, null, 500, 2000);

                _clients.Add(OperationContext.Current);
            }
        }
    }

    public void Unsubscribe()
    {
        RemoveClient(OperationContext.Current);
    }

    private static void TimerTick(object state)
    {
        Task.Factory.StartNew(() =>
        {
            double temperature = _random.Next(-200, 400) * 0.1d;

            // Copy the clients array because it can be modified while been read var clients = _clients.ToArray();
            foreach (var client in clients)
            {
                try {
                    var channelState = client.Channel.State;
                    if (channelState == CommunicationState.Opened)
                    {
                        var callbackChannel = client.GetCallbackChannel<ITemperatureServiceCallback>();
                        callbackChannel.PushTemperature(temperature);
                    }
                    else {
                        RemoveClient(client);
                    }
                }
                catch (TimeoutException)
                {
                    RemoveClient(client);
                }
                catch (Exception)
                {
                    _updateTimer.Dispose();
                }
            }
        });
    }

    private static void RemoveClient(OperationContext client)
    {
        lock (_locker)
        {
            _clients.Remove(client);
        }
    }
}

In this implementation you have to focus on two things :

  • OperationContext.Current
  • client.GetCallbackChannel

OperationContext.Current gives us information about the client that has called the current executing service method. client.GetCallbackChannel gives us an instance of the service callback to use in order to send data from the server to the client.

Then we expose our service using a .svc file. In my example the svc file looks like this :

<%@ ServiceHost Language="C#" Debug="true" Service="SilverlightReactivePushServer.Web.TemperatureService" CodeBehind="TemperatureService.svc.cs" %>

Finally we need to configure the binding in the web.config file, here’s how the serviceModel section is looking :

<system.serviceModel>
  <extensions>
    <bindingExtensions>
      <add name="pollingDuplexHttpBinding"
           type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
     </bindingExtensions>
  </extensions>
  <bindings>
  <pollingDuplexHttpBinding>
    <binding name="multipleMessagesPerPollPollingDuplexHttpBinding"
                   duplexMode="MultipleMessagesPerPoll"
                   maxOutputDelay="00:00:00.500"
                   sendTimeout="00:00:02.000"
                   closeTimeout="00:00:02.000"/>
    </pollingDuplexHttpBinding>
  </bindings>
  <services>
    <service name="SilverlightReactivePushServer.Web.TemperatureService">
      <endpoint address="" binding="pollingDuplexHttpBinding"
                bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding"
                name="pollingDuplex"
                contract="SilverlightReactivePushServer.Web.ITemperatureService" />
      <endpoint address="mex" binding="mexHttpBinding" name="mex" contract="IMetadataExchange" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

We’re now done for the server side.

Using the service in Silverlight

First let’s add a reference to our previously defined service and let’s define it’s namespace as TemperatureServer.

The tool used by Visual Studio to generate the service proxy correctly generates the needed classes but doesn’t generate a proper ServiceReferences.ClientConfig file. By not proper I just mean empty.

So let’s configure the reference by ourselves (don’t forget to replace the endpoint addresses by yours).

<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="httpPolling">
          <binaryMessageEncoding />
          <pollingDuplex duplexMode="MultipleMessagesPerPoll" />
          <httpTransport transferMode="StreamedResponse" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:1614/TemperatureService.svc"
                binding="customBinding"
                bindingConfiguration="httpPolling"
                contract="TemperatureServer.ITemperatureService" />
    </client>
  </system.serviceModel>
</configuration>

We will now create a TemperatureService class that will wrap the WCF service proxy as an observable service. This class is a singleton (be free to use any dependency injection libraries instead of a singleton) that will be used into every modules (Celsius and Fahrenheit) to call the web service.

Here’s how it looks like :

public class TemperatureService {
    private static readonly TemperatureService _temperature = new TemperatureService();

    private TemperatureServiceClient _client;
    private readonly IObservable<double> _temperatures; 

    protected TemperatureService()
    {
        _temperatures = Observable.Create<double>(observer =>
        {
            if (_client == null)
            {
                _client = new TemperatureServiceClient();
                _client.SubscribeAsync();
            }

            _client.PushTemperatureReceived += (s, a) => observer.OnNext(a.temperature);

            return () => { };
        });
    }

    public IObservable<double> Temperatures
    {
        get { return _temperatures; }
    }

    public static TemperatureService Current
    {
        get { return _temperature; }
    }
}

In the constructor we create an observable that will call the SubscribeAsync method of our WCF service once. Then, every time our observable of temperatures is subscribed to, we register to the PushTemperatureReceived event of the WCF proxy class. The lambda we use here to register to the event just pushes the temperature received into the observable by using the OnNext method of its observer. And that pretty much it, all the magic happens here. Now every time we received a temperature from the server, it will be available to all the subscribers of the observable.

I created two views for displaying the temperatures in Celcius and Farenheit. Here’s their XAML code :

<UserControl x:Class="SilverlightReactivePushServer.Views.CelsiusView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding CelsiusTemperature}" />
        <TextBlock Text=" °C" />
    </StackPanel>
</UserControl>
<UserControl x:Class="SilverlightReactivePushServer.Views.FahrenheitView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding FahrenheitTemperature}" />
        <TextBlock Text=" °F" />
    </StackPanel>
</UserControl>

Here’s how I used it in the main view :

<UserControl x:Class="SilverlightReactivePushServer.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:SilverlightReactivePushServer.Views">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <ComboBox Grid.ColumnSpan="2" HorizontalAlignment="Center" ItemsSource="{Binding DuplexTypes}" SelectedItem="{Binding DuplexType, Mode=TwoWay}" />
        <views:CelsiusView Grid.Column="0" Grid.Row="1"  HorizontalAlignment="Right" Margin="5" />
        <views:FahrenheitView Grid.Column="1" Grid.Row="1"  HorizontalAlignment="Left" Margin="5" />
    </Grid>
</UserControl>

Each of the temperature views has it’s own view model. Here’s their implementation :

public class CelsiusViewModel : INotifyPropertyChanged {
    private double _celsiusTemperature;
    public event PropertyChangedEventHandler PropertyChanged;

    public double CelsiusTemperature
    {
        get { return _celsiusTemperature; }
        private set {
            if (_celsiusTemperature != value)
            {
                _celsiusTemperature = value;
                RaisePropertyChanged("CelsiusTemperature");
            }
        }
    }

    public CelsiusViewModel()
    {
        TemperatureService.Current.Temperatures.Subscribe(t => CelsiusTemperature = t);
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class FahrenheitViewModel : INotifyPropertyChanged {
    private double _fahrenheitTemperature;

    public event PropertyChangedEventHandler PropertyChanged;

    public double FahrenheitTemperature
    {
        get { return _fahrenheitTemperature; }
        private set {
            if (_fahrenheitTemperature != value)
            {
                _fahrenheitTemperature = value;
                RaisePropertyChanged("FahrenheitTemperature");
            }
        }
    }

    public FahrenheitViewModel()
    {
        TemperatureService.Current.Temperatures.Subscribe(t => FahrenheitTemperature = (9 / 5) * t + 32);
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

That’s pretty much it. Everything already works fine with that. Each of the view models subscribe to the Temperatures observable defined in the TemperatureService class we created earlier and just refreshes their own temperature property with some conversion calculation for the Fahrenheit view model.

Conclusion

We have now a duplex service used by Silverlight that will work over the classical http protocol. We use the reactive extensions to simplify our code and to share the same network resources across different view models that needed the same information at the same time.

I find it pretty sexy and you ?

As usual you can find the code used in the example in my skydrive.

Hope you liked it.

Updated:

Leave a Comment