DevelopMENTAL Madness

Monday, June 08, 2009

Prism for Silverlight 2: Taking ‘Hello World’ to a Whole New Level

The World’s Most Over-engineered “Hello World” Demo

Download Source

I wanted to build a demo/guidance application for Silverlight that merges everything I want to accomplish in a Silverlight 2 application. These were my goals:

  • WCF integration
  • Design-time data binding
  • Independent, decoupled modules
  • Commanding support

I chose to use the Composite Application Library for WCF/Silverlight (aka Prism 2) since it came out of the box with commanding support and a framework for pluggable modules. Prism 2 was developed by Microsoft’s Patterns and Practices (PnP) group.

I’ve posted a copy of my solution for anyone who’s interested in looking it over and maybe a more simple example than the Reference Implementation (RI) provided by the PnP team. The biggest part of this exercise for me was the integration of WCF with Prism. The RI provided with the Prism documentation uses a local XML file as a data source. For me this was disappointing. I had been hoping for a more complete RI since Silverlight really isn’t worth anything as a LOB platform without integration with web services.

Now that I’ve finished, I’d like to share both my sample and what I’ve learned.

What This Post Is Not

This post is not a detailed document on Silverlight, WCF, Prism or DI/IoC. These are all used and are relevant to the discussion, but in the interest of time these are all beyond the scope of this post. I have tried to provide helpful links to information along the way, but for more information on related technologies beyond the scope of this post consult the links I have provided as well as google.

Solution Structure

I tried to model the solution structure to mirror what would be realistic of an enterprise-level application. I wanted minimal project dependencies and a modular design. Here’s a description of the structure and why.

Modules

Implicit in my goals was that I did not want my modules referencing each other. So I created a Silverlight Class Library project which contains my interfaces, base and utility classes named HelloWorld.Interfaces. This is the only project referenced by the other class library projects.

Silverlight Project

The only project which references the modules is the Silverlight Application Project named HelloWorld.Silverlight. At this point I am not dynamically loading assemblies since I don’t actually need it yet. I may add it down the road, but for now my needs are met.

Client/Server Shared Files

Also, I wanted to be able to use the same code base on the server as well as the client. I didn’t want to have to copy/paste code for interfaces and my data model between layers. So for objects and interfaces which are used by both the client and the server I created the class files in the server projects (Windows Class Library and ASP.NET Web Application) and then added the same files to the Silverlight projects via Add Existing File… –> Add as link…

WCF Communication

Server

There really isn’t much to say about the service itself. For my purposes the interface is the important part:

using System;
using System.ServiceModel;
using HelloWorld.Model;
 
namespace HelloWorld.Interfaces
{
    [ServiceContract(Namespace = "http://helloworld.org/messaging")]
    public interface IMessageService
    {
        [OperationContract]
        Message UpdateMessage(string message);
 
        [OperationContract]
        Message GetMessage();
    }
 
    /// <summary>
    /// Async pattern implementation for IMessageService
    /// </summary>
    /// <remarks>
    /// Found this idea here: http://ayende.com/Blog/archive/2008/03/29/WCF-Async-without-proxies.aspx
    /// 
    /// Because I declared Namespace on IMessageService both Name and Namespace were required for this to work,
    /// if I don't use Namespace on the Sync version I can leave it off the Async version.
    /// </remarks>
    [ServiceContract(Name = "IMessageService", Namespace = "http://helloworld.org/messaging")]
    public interface IMessageServiceAsync
    {
        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginUpdateMessage(string message, AsyncCallback callback, object asyncState);
        Message EndUpdateMessage(IAsyncResult result);
 
        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginGetMessage(AsyncCallback callback, object asyncState);
        Message EndGetMessage(IAsyncResult result);
    }
}

By defining a second interface to represent the service contract as an asynchronous pattern the client can bind directly to the asynchronous interface without having to define a wrapper class on the client side.

Client

For me this was the biggest part of this exercise. Originally, I used the “Add Service Reference…” wizard to add a reference to my service. However, this had several shortcomings I wasn’t happy about which all boiled down to one deal-breaker for me: any module using the interface would require a reference to my services project.

1) I couldn’t control the namespace used. Yes, it asks me for the namespace, but that is appended to the default namespace of the project where the service client is added. I’d rather have complete control over the namespace.

2) The wizard will reuse the model classes in my other projects if I ask it to, this is a plus. However, it won’t reuse my interfaces. The reason this is a problem is that I am tied to the client that is generated, so can’t just reference my interfaces project I have to either place my service client in my interfaces project or reference the service project from each other project.

3) The asynchronous event model used is simple and easy to use, but it’s tied to concrete EventArgs implementations. This in and of itself is not the problem. My problem with this is that again, I am tied to this one file and its location in my solution. I’m required to have references to it across my solution and so I am really only pretending to program to a loosely coupled set of interfaces. I’m really still tied to a concrete implementation. This may seem silly to some, because I could still use the interfaces and if I really needed to replace the implementation I could. But it just felt wrong.

I understand the constraints behind the output from creating a service reference, but there was just so much maintenance work involved in using it in a loosely-coupled way that once I found an alternative that I dumped the service client implementation.

So I was grateful to find this post by Ayende Rahien. All that is required is that I create a link to the interfaces defined in my Windows Class Library project (Silverlight requires this, WPF could just reference the Windows Class Library directly). Then to create a proxy for use on my client I just do this:

IMessageServiceAsync service = new ChannelFactory<IMessageServiceAsync>("BasicHttpBinding_IMessageEndPoint").CreateChannel();

Nice, clean and no maintenance. I don’t even have to regenerate the proxy when the interface changes.

However, there is a tradeoff.

Dispatcher

The nice thing you don’t have to worry about when using the service client proxy is threading. I haven’t figured out why yet, but something to do with the way the events are handled by the generated proxy means you can just register for the XXXCompleted event then call the XXXAsync method and directly update your UI from the XXXCompleted event handler. This is called the Asynchronous Event Model. However, using ChannelFactory<T> means you’re not using events, you’re using callbacks – what I’ll call the Standard Asynchronous Model (aka BeginXXX and EndXXX).

This is where Dispatcher comes in. Dispatcher is a class located in System.Windows.Threading. Without getting into details, Xaml based apps (Silverlight and WPF) are single-threaded. But Silverlight requires you to execute blocking calls asynchronously. So when your asynchronous method completes you’re on a different thread. Dispatcher allows you to execute a System.Delegate or System.Action back on the UI Thread. This way you can make updates to your UI based on the result of your service call.

Like I said, if you’re using the service reference proxy this is handled for you. Otherwise you need to do this on your own. So here’s what I’ve done:

using System;
 
namespace HelloWorld.Interfaces
{
    /// <summary>
    /// Abstraction for System.Windows.Threading.Dispatcher
    /// </summary>
    /// <remarks>
    /// I got this from a thread on Stackoverflow.com. This allows me to perform testing w/o the
    /// Silverlight testing framework. The benefit is that it completely decouples the code from the UI.
    /// http://stackoverflow.com/questions/486758/is-wpf-dispatcher-the-solution-of-multi-threading-problems
    /// </remarks>
    public interface IDispatcher
    {
        void BeginInvoke(Action action);
        void BeginInvoke(Delegate method, params object[] args);
    }
}

This is so I can create an adapter for Dispatcher to be used when the application is running, then I can also write tests using a MockDispatcher which means my tests can then execute on a single thread because my MockDispatcher would be single threaded. This wasn’t my idea, I was just able to find it thanks to StackOverflow.com.

Here’s my implementation of IDispatcher:

using System;
using System.Windows;
 
namespace HelloWorld.Interfaces
{
    /// <summary>
    /// A wrapper for System.Windows.Threading.Dispatcher
    /// </summary>
    /// <remarks>
    /// This version requires a Dispatcher to exist, which
    /// means this class is tied to the UI. For testing,
    /// create a Mock IDispatcher which will invoke the
    /// action or delegate on the same thread since there
    /// is no worring about the UI.
    /// </remarks>
    public class DispatcherFacade : IDispatcher
    {
        public DispatcherFacade(bool isHtmlEnabled)
        {
            /* this value needs to be passed in when it is initialized 
             * because for some reason calling HtmlPage.IsEnabled always 
             * returns false in this class. I'll need to look into the
             * source code of HtmlPage.IsEnabled to find out why this 
             * happens
             */ 
            isEnabled = isHtmlEnabled;
        }
 
        #region IDispatcher Members
        private bool isEnabled = false;
 
        public void BeginInvoke(Action action)
        {
            if (isEnabled == false)
            {
                action.Invoke();
                return;
            }
 
            Deployment.Current.Dispatcher.BeginInvoke(action);
        }
 
        public void BeginInvoke(Delegate method, params object[] args)
        {
            if (isEnabled == false)
            {
                method.DynamicInvoke(args);
                return;
            }
 
            Deployment.Current.Dispatcher.BeginInvoke(method, args);
        }
 
        #endregion
    }
}

Now that w'e’re looking at this, you may notice something a bit hackish. You’ll notice that my ctor has a Boolean parameter named isHtmlEnabled. And you’re asking why doesn’t this guy just reference System.Windows.Browser.HtmlPage.IsEnabled? Well, origionally I did. But inside this class (project?) the value returned was always False. Since my dispatcher is being used as a Singleton I just get the value of HtmlPage.IsEnabled during the StartUp event of App and pass it into my Dispatcher. Since this was easy to do and doesn’t cause me any problems I’m fine with this. If you actually know why this happens and have a suggestion, please let me know. But I posted a question about this on Silverlight.net forums and didn’t get an answer, so this is what I’ve got.

Now, when the app is actually running I can safely update the UI thread. When the app isn’t running (Test run or Design mode) I can directly execute the passed in Action or Delegate. Here’s how to use it:

private void GetMessageComplete(IAsyncResult result)
{
    // get result
    Message output = _service.EndGetMessage(result);
 
    // need dispatcher to execute on UI thread
 
    // update Message on UI thread
    _dispatcher.BeginInvoke(() => UpdateUIThread(output));
}
 
public void UpdateUIThread(Message message)
{
    this.Message = message;
}

Design-time Data Binding

This was an important issue for me for a couple of reasons:

  • I suck at UI design and can use every bit of help I can get
  • I’m working with a Flash developer and I’d like to make things as simple as possible to remove as many obstacles as possible.
  • I want to know my bindings work without having to compile and load up the web browser.

I originally saw Jonas Follesoe demonstrate this last summer in a TechEd 2008 webcast. He created a ServiceLocator which used Ninject to load up a mock service provider during design time. By adding the ServiceLocator as a static global resource to App.xaml the Silverlight designer initializes ServiceLocator and then displays the data in your preview window (NOTE: this doesn’t work in the VS 2008 preview window just in Blend, but from what I read about VS 2010 it might work there).

ServiceLocator.cs:

using System.Windows.Browser;
using HelloWorld.Interfaces;
using HelloWorld.Interfaces.Views;
using HelloWorld.Module.Views;
using HelloWorld.Services;
using Microsoft.Practices.Unity;
 
namespace HelloWorld.Module
{
    /// <summary>
    /// Provides access to ViewModels
    /// </summary>
    /// <remarks>
    /// The purpose of this class is to enhance the design-time experience
    /// by providing a default instance of UnityContainer with Mock 
    /// services which can function at design time to provide the 
    /// data required by the view
    /// 
    /// This class exists exists in the scope of the module to avoid
    /// conflicts (if any) between modules and their IUnityContainer instances.
    /// </remarks>
    /// <example>
    /// From view:
    /// &lt;UserControl
    ///     x:Class="...."
    ///        xmlns="...."
    ///        xmlns:x="..."
    ///        DataContext="{Binding Path=HelloWorldViewModel, Source={StaticResource resourceLocator}}"
    /// &gt;
    ///        &lt;UserControl.Resources&gt;
    ///            &lt;local:ServiceLocator x:Key="resourceLocator" /&gt;
    ///        &lt;/UserControl.Resources&gt;
    /// 
    /// &lt/UserControl&gt;
    /// </example>
    public class ServiceLocator
    {
        private static IUnityContainer BuildDesignTimeContainer()
        {
            IUnityContainer container = new UnityContainer();
 
            // if we're live (aka not in the designer/Blend) the Bootstrapper 
            // will setup the container
            if (HtmlPage.IsEnabled)
                return container;
 
            // register required objects 
            container.RegisterInstance<IDispatcher>(new DispatcherFacade(false));
            container.RegisterType<IHelloWorldPresenter, HelloWorldPresenter>();
 
            // register mock services so they can be used in Design Mode
            container.RegisterType<IMessageServiceAsync, MockMessageService>();
 
            return container;
        }
 
        private static IUnityContainer _container;
        public static IUnityContainer Container
        {
            get
            {
                if (_container == null)
                {
                    // setup container
                    _container = BuildDesignTimeContainer();
                }
 
                return _container;
            }
            set
            {
                _container = value;
            }
        }
 
        /// <summary>
        /// Public property for the ViewModel which
        /// can be referenced by the View as a Resource
        /// </summary>
        public IHelloWorldPresenter HelloWorldViewModel
        {
            get
            {
                return Container.Resolve<IHelloWorldPresenter>();
            }
        }
    }
}

HelloWorldView.xaml:

<UserControl x:Class="HelloWorld.Module.Views.HelloWorldView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cmd="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
    xmlns:local="clr-namespace:HelloWorld.Module"
    DataContext="{Binding Path=HelloWorldViewModel, Source={StaticResource serviceLocator}}"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignWidth="507"
>
    
    <UserControl.Resources>
        <local:ServiceLocator x:Key="serviceLocator" />
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        
        <StackPanel Grid.Row="0" Grid.ColumnSpan="2">
            <TextBlock Text="{Binding Path=Message.Value}" Foreground="Green" 
                HorizontalAlignment="Center" FontSize="24" 
                FontWeight="Bold" />
            <TextBlock Text="{Binding Path=Message.Date}" 
                FontWeight="Bold" HorizontalAlignment="Center" Foreground="Red" />
        </StackPanel>
    
        <TextBox Text="{Binding Mode=TwoWay, Path=Message.Value}" Grid.Row="1" Grid.Column="0" />
        <Button Grid.Row="1" Grid.Column="1" cmd:Click.Command="{Binding Path=UpdateMessage}"
            cmd:Click.CommandParameter="{Binding Message}" >
            <TextBlock><Run Text="Update Message"/></TextBlock>
        </Button>
    </Grid>
</UserControl>

The difference between Jonas’ DiveLog application is that my views aren’t in the same project as App.xaml, so I need to add a reference to ServiceLocator as a local resource in my view file. A small difference, but important to note nonetheless.

Also, I need to update the Container property during my module’s Initialize method so I can maintain a reference to my runtime UnityContainer.

Conclusion

I’ve really learned a lot from this exercise and I hope it helps others. I know I’ve still got quite a bit to learn but I figure this is a good starting point for anyone wanting to take advantage of Prism and it goes a bit further than the PnP RI. I welcome any comments on improvements or questions. Over the summer I will be putting this all to a real-world test and I will continue to provide updates on what I’ve learned.

Download Source

Labels: , , , ,