none
Soma Rx Blog Sample

    General discussion

  • Several people have asked for the source of the sample on Soma’s blog about Rx. The sample explains how to write a prototypical  Silverlight-based AJAX application, the killer scenario for Rx, where you have to mash up events from the UI with asynchronous calls to several Web services. Specifically, the sample makes three concurrent calls to the Bing translation service and shows the first two results that come back.

    To run the sample, just create a new Silverlight project. No need to add a Web site for it. Just the basic project with an automatically generated test page is sufficient.

    Since I am a complete kludge when it comes to UI design, I do not even try to make it look fancy. Just a TextBox for the user input and three TextBlocks to show the results of the translations of the input into various languages. Here is the contents of my MainPage.xaml:

    <UserControl x:Class="SomaBlogRx.MainPage"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns
    :mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

      <Grid x:Name="LayoutRoot">

            <StackPanel>

                <TextBox Name="_input" Width="600" Height="20" Background="Aquamarine"></TextBox>

                <TextBlock Text="Dutch"></TextBlock>

                <TextBlock Name="_dutch" Text="..."></TextBlock>

                <TextBlock Text="French"></TextBlock>

                <TextBlock Name="_french" Text="..."></TextBlock>

                <TextBlock Text="Spanish"></TextBlock>

                <TextBlock Name="_spanish" Text="..."></TextBlock>

            </StackPanel>

        </Grid>

    </UserControl>

    Any suggestions for more a more appealing UI design are very welcome.

    The first step is to add a service reference to the Bing translation service. You will have to sign up for a Bing API key at  http://msdn.microsoft.com/en-us/library/dd251056.aspx. When you have your key, add a service reference to  http://api.bing.net/search.wsdl?AppID=[insert your own key here]&Version=2.2 using BingService for the namespace. In the advanced settings choose System.Array for the collection type and unselect “Reuse types in reference assemblies”. If this all works, you will have a BingService entry in your Service References folder in your project:

     

    Once we have the service reference to Bing, we will expose the raw generated service as an async subject  AsyncSubject<TranslationResponse> that we hook up to the SearchCompleted event of the SearchAsync call. Do not forget to insert your own Bing API key in the code below.

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using SomaBlogRx.BingService;

     

    namespace SomaBlogRx

    {

        /// <summary>

        /// Observable wrapper over generated Bing service reference.

        /// </summary>

        public static class Bing

        {

            /// <summary>

            /// Pick first translation from response.

            /// </summary>

            public static string GetTranslation(this TranslationResponse response)

            {

                var translation = "";

                if (response != null) translation = response.Results[0].TranslatedTerm;

                return translation;

            }

     

            const string AppId = [insert your own key here];

     

            /// <summary>

            /// Strongly typed null pointer.

            /// </summary>

            public readonly static TranslationResponse NoResult;

     

            /// <summary>

            /// Asynchronously return the translated text for the given text supplied,

            /// matching the destination language.

            /// </summary>

            /// <param name="text">The text that is to be translated.</param>

            /// <param name="sourceLanguage">The source language as a language code. </param>

            /// <param name="destinationLanguage">The destination language as a language code.</param>

            public static IObservable<TranslationResponse> Translate

                     (this string text, string sourceLanguage, string destinationLanguage)

            {

                var subject = new AsyncSubject<TranslationResponse>();

                var service = new BingPortTypeClient();

                var request = new SearchRequest

                {

                    AppId = AppId,

                    Translation = new TranslationRequest

                    {

                        SourceLanguage = sourceLanguage,

                        TargetLanguage = destinationLanguage,

                    },

                    Query = text,

                    Sources = new[] { SourceType.Translation },

                };

                service.SearchCompleted += (sender, e) =>

                {

                    if (e.Cancelled) subject.OnCompleted();

                    else if (e.Error != null) subject.OnError(e.Error);

                    else subject.OnNext(e.Result.Translation);

                };

                service.SearchAsync(request);

                return subject.Hide().ObserveOnDispatcher(); // move back to dispatcher thread.

            }

        }

    }

    Before we return from the wrapper, we hide the IObserver aspect of the subject and make sure that we transition back to the Dispatcher thread to avoid the dreaded “not on the UI thread” exceptions.

    All the real orchestration is done inside MainPage.xaml.cs. First we expose the KeyUp events of the TextBox _input as an observable collection of type  IObservable<IEvent<KeyEventArgs>> using the IObservable.FromEvent helper, making sure that the subscription happens on the correct thread.

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Windows.Controls;

    using System.Windows.Input;

     

    namespace SomaBlogRx

    {

        public partial class MainPage : UserControl

        {

            /// <summary>

            /// Return observable collection of KeyUp events on _input

            /// making sure that subscription happens on the dispatcher thread.

            /// </summary>

            IObservable<IEvent<KeyEventArgs>> Input()

            {

                return Observable.FromEvent<KeyEventArgs>(_input, "KeyUp").SubscribeOnDispatcher();

            }

    Since we do not want to invoke a search upon each keystroke, we throttle the input stream such that it only fires an event when the user has paused typing for ½ second. The simple LINQ query transforms the observable collection of KeyUp events into an observable collection of strings containing the Text of the input after each KeyUp event:

     

            public MainPage()

            {

                InitializeComponent();

     

                // Read words from _input once user pauses for 1/2 second

                var words = (from _ in Input() select _input.Text).Throttle(TimeSpan.FromSeconds(0.5));

    The core of the sample fires of three Bing searches let dutch = text.Translate("en", "nl"), …, for the word that the user has typed, and then uses a join pattern to wait for the first two of the three results to come back. You can think of join patterns as Outlook rules where instead of specifying what happens when a new email comes in, you specify rules for what happens when a new notification arrives on an observable collection. If the user types a new word before the results of the service call have arrived, we ignore the results using the TakeUntil(words) function.

     

                // Fire off three concurrent calls to the Bing service

                // Wait for two out of three to return,

                // or until the user types the next word

                var translations =

                    from text in words

                    let dutch = text.Translate("en", "nl")

                    let french = text.Translate("en", "fr")

                    let spanish = text.Translate("en", "es")

                    from results in Observable.Join

                       (dutch.And(spanish).Then((d, s) =>

                            new { Dutch = d, French = Bing.NoResult, Spanish = s })

                       , dutch.And(french).Then((d, f) =>

                            new { Dutch = d, French = f, Spanish = Bing.NoResult })

                       , french.And(spanish).Then((f, s) =>

                            new { Dutch = Bing.NoResult, French = f, Spanish = s })

                       ).TakeUntil(words)

                    select results;

    Finally, we can subscribe to the observable collection of translations and update the UI with the translations provided by Bing:

                // Update the UI with the results

                translations.Subscribe(w =>

                {

                    _dutch.Text   = w.Dutch.GetTranslation();

                    _french.Text  = w.French.GetTranslation();

                    _spanish.Text = w.Spanish.GetTranslation();

                });

            }

        }

    }

    While this is a much simplified example, it capture the essential design pattern of many AJAX applications where we need to react to UI events, orchestrate a number of concurrent webservice calls as the result of these UI events and update the UI with the results.

    Friday, November 27, 2009 7:35 AM

All replies

  • This is some great stuff. Examples like this are very helpful. Thanks for posting this!
    Friday, November 27, 2009 3:37 PM
  • where can we find the BingService.dll i have downloaded the bing api 2.0 but can't find the dll to reference it.
    Saturday, November 28, 2009 7:53 AM
  • Did you add a service reference to  http://api.bing.net/search.wsdl?AppID=[insert your own key here]&Version=2.2 

    Saturday, November 28, 2009 8:10 AM
  • I've implemented it on my own with some modifications. The full sourcecode as SL3 Visual Studio solution, a live demo hosted on Azure and the description how to use the code can be found here: http://www.minddriven.de/?p=550 . Please don't forget to request an AppId for using the Bing Web Service API from http://www.bing.com/developers/createapp.aspx and to put it in the BingService.AppId field (BingService.cs) in my sources.

    Thanks Eric and the Rx team for giving us this example.
    ~ Matthias
    Saturday, November 28, 2009 8:55 AM
  • really thnx for the example :)

    Rishi
    (www.techtreeblog.com)

    Wednesday, December 02, 2009 1:08 PM
  • Hi,

    it would be nice I you could describe how this implementation changes with the new IScheduler.

    If I understand the video correctly we don't really want to do .ObserveOnDispatcher() and .SubscribeOnDispatcher() and the line with .Throttle() needs also a change.

    Regards,
      forki
    Monday, December 21, 2009 9:43 AM