DevelopMENTAL Madness

Tuesday, March 10, 2009

Silverlight and Prism: Decoupling the WCF Client Proxy

I’m in the process of creating the world’s most complex “Hello World” Silverlight application. Hopefully, for once, I’ll see it through to the end and also post the source and an insightful blog entry to help everybody who’s struggling to do the same. Anyone who follows my blog knows that I don’t always follow through on a series to the end. But, “I can dream can’t I?”.

Anyhow, I’ve taken the recent release of Prism v2, which introduced Silverlight support, to dig in and get to know both Prism and Silverlight. As background, I have one production WFP application under my belt. We didn’t use Prism, although I looked at it. Instead, I had modeled it after Jonas Follesoe’s Presentation Model implementation which he had demoed at TechEd 2008. For the project, Prism seemed a little like overkill. But for my upcoming “Hello World” application it’s perfect :P.

My goals for this demo application are that it would be fully testable and modular. It would incorporate WCF with Silverlight 2 and the tests could be run without the Silverlight Test Framework. About the last requirement, I have nothing against the SL Test Framework. In fact I see it has great potential, but I think as a rule if my code is really decoupled then it shouldn’t require the framework for testing. That said I really think it could be useful for functional testing and for compatibility testing between browsers.

UPDATE 3/12/09: While looking for a way to avoid using partial classes and explicit interface implementations I ran into a post about avoiding generated proxies I thought it seemed cleaner. The source code is a bit of a work in progress, so it doesn’t have the solution described here in this post. It has the one which doesn’t have proxies. But it does finally include an example of testing without using the Silverlight Test Framework which I’ll blog about soon.

UPDATE 5/10/09: I've since finished working through this and have an updated post along with source code. This post documents where I started out and when read together with my more recent post shows the progression of my understanding of recommended practices.

Problem

One thing that has dogged me in this exercise has been the WCF client proxy generated when I add a service reference to my Silverlight project. I ran into a couple things that either didn’t work, or just didn’t smell right (eg. didn’t quite meet my goals listed above).

I like that the service generates a proxy class for me, I also like that it creates an interface for me as well. However, the first problem I ran into was trying to use the interface. Here’s what the generator creates:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://helloworld.org/messaging", ConfigurationName="Web.Services.HelloWorldMessageService")]
public interface HelloWorldMessageService {
    
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessage", ReplyAction="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessageResponse")]
    System.IAsyncResult BeginUpdateMessage(string message, System.AsyncCallback callback, object asyncState);
    
    void EndUpdateMessage(System.IAsyncResult result);
    
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://helloworld.org/messaging/HelloWorldMessageService/GetMessage", ReplyAction="http://helloworld.org/messaging/HelloWorldMessageService/GetMessageResponse")]
    System.IAsyncResult BeginGetMessage(System.AsyncCallback callback, object asyncState);
    
    HelloWorld.Model.Message EndGetMessage(System.IAsyncResult result);
}

And for reference here’s my service definition:

public class Message
{
    public DateTime Date { get; set; }
 
    public String Value { get; set; }
}
 
[ServiceContract(Namespace = "http://helloworld.org/messaging")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class HelloWorldMessageService
{
    private static Message _message = new Message { Value = "Hello from WCF", Date = DateTime.Now };
 
    [OperationContract]
    public void UpdateMessage(string message)
    {
        _message.Value = message;
        _message.Date = DateTime.Now;
    }
 
    [OperationContract]
    public Message GetMessage()
    {
        return _message;
    }
}

My first WPF project did not require asynchronous handling – you may scoff, but it only operated on small local files, converting them from one format to another and so asynchronous communication would have been better but would not have made any difference to the end user.

When I first attempted to work with asynchronous communication (first in a xaml-type application) I was a little flustered. All the examples I could find (in books or online) used the XXXAsync() methods and Completed events generated in the proxy class. But my problem was that these where not part of the interface generated for the class, so I couldn’t use them.

The interface includes methods which are standard to the asynchronous programming model. I’ve used them before for network communication, file system operations and other common operations which often block the current thread. So here’s what my first attempt looked like:

public HelloWorldPresenter(IHelloWorldView view, IMessageServiceClient service)
{
    // store dependencies
    _service = service;
    
    // load data asyncronously
    IAsyncResult result = _service.BeginGetMessage(new AsyncCallback(BeginGetMessageComplete), null);
    
    // do some other stuff
}
 
private void BeginGetMessageComplete(IAsyncResult result)
{
    // get result
    Message output = _service.EndGetMessage(result);
 
    this.Message = output;
}

But when I ran it I got the following error:

UnauthorizedAccessException: Invalid cross-thread access.

Which translates to, “you can’t update the UI thread from a background thread, dummy”.

Solution

After digging (and missing the solution a couple of times), I found that I had to use the System.Threading.Dispatcher class to correct the issue. The reason I had missed it was because I didn’t understand Dispatcher (I did say I only had one WPF app under my belt and it didn’t use async methods). I remember reading during my search suggestion to store a reference to Dispatcher in the container so I could resolve it, but it didn’t seem right. But once I read the documentation I realized that this was OK.

As it turns out there is only one Dispatcher in your application (you can create more, but everything I’ve read says you should have a really good reason for it). The Dispatcher is attached to the CurrentThread and so that’s why it’s a good way to get back to the UI thread. So when your application starts, you register a reference to Dispatcher with your container. There’s a couple ways to access it, but I’m grabbing it from the view I’m registering as my RootVisual object.

public class Bootstrapper : UnityBootstrapper
{
    protected override IModuleCatalog GetModuleCatalog()
    {
        // setup module catalog
    }
 
    protected override DependencyObject CreateShell()
    {
        // calling Resolve instead of directly initing allows use of dependency injection
        Shell shell = Container.Resolve<Shell>();
 
        // set shell as RootVisual
        Application.Current.RootVisual = shell;
 
        // store reference to shell.Dispatcher so background threads can update UI
        Container.RegisterInstance<Dispatcher>(shell.Dispatcher);
 
        return shell;
    }
}

Now my presenter looks like this:

public HelloWorldPresenter(IHelloWorldView view, HelloWorldMessageService service, IUnityContainer container)
{
    // store dependencies
    _service = service;
    _container = container;
    
    // initialize locals
    UpdateMessage = new DelegateCommand<string>(this.OnUpdateMessageExecute, this.OnUpdateMessageCanExecute);
    
    // load data asyncronously
    IAsyncResult result = _service.BeginGetMessage(new AsyncCallback(BeginGetMessageComplete), null);
    
    // set reference to view (needed for RegionManager)
    View = view;
 
    // set the view's model to the presenter
    View.Model = this;
}
 
private void BeginGetMessageComplete(IAsyncResult result)
{
    // get result
    Message output = _service.EndGetMessage(result);
 
    // need dispatcher to execute on UI thread
    Dispatcher dispatcher = _container.Resolve<Dispatcher>();
 
    // update Message on UI thread
    dispatcher.BeginInvoke(() => this.Message = output);
}

I can resolve a reference to Dispatcher and either update my model directly as I’ve done here or I could also make a method call and pass in the value of “output” as an argument. Here I’ve chosen to update the model directly since it is a simple one-liner.

Functionally, this meets my goals, but as an exercise I also wanted to make it so my modules didn’t have to reference each other. The Prism documentation recommends against it (if you can avoid it) and since I was trying to be thorough I decided to remove references between modules. First I created a new Silverlight Class Library project and called it “Interfaces”. Then I defined the following interface:

public interface IMessageServiceClient
{
    IAsyncResult BeginGetMessage(AsyncCallback callback, object asyncState);
    string EndGetMessage(IAsyncResult result);
 
    IAsyncResult BeginUpdateMessage(string message, AsyncCallback callback, object asyncState);
    void EndUpdateMessage(IAsyncResult result);
}

Technically a module IS a Silverlight Class Library, but since not every Silverlight Class Library is a module I think I can get away with this and still meet the Prism guidelines.

The next step is to apply the interface to my WCF client proxy. This is easy since it is defined as a partial class. So all I had to do was this:

public partial class HelloWorldMessageServiceClient : IMessageServiceClient
{
    #region IMessageServiceClient Members
 
    IAsyncResult IMessageServiceClient.BeginGetMessage(AsyncCallback callback, object asyncState)
    {
        return ((HelloWorldMessageService)this).BeginGetMessage(callback, asyncState);
    }
 
    string IMessageServiceClient.EndGetMessage(IAsyncResult result)
    {
        return ((HelloWorldMessageService)this).EndGetMessage(result);
    }
 
    IAsyncResult IMessageServiceClient.BeginUpdateMessage(string message, AsyncCallback callback, object asyncState)
    {
        return ((HelloWorldMessageService)this).BeginUpdateMessage(message, callback, asyncState);
    }
 
    void IMessageServiceClient.EndUpdateMessage(IAsyncResult result)
    {
        ((HelloWorldMessageService)this).EndUpdateMessage(result);
    }
 
    #endregion
 
}

You’ll have to do this for every WCF OperationContract in your service, so you can decide if this is worth it to you because if you modify your service you’ll have to do more than just execute “Update Service Reference” on your web service reference in the project.

The other trick in getting this to work is that you need a Silverlight project which has a copy of your object model in it. If you didn’t know already, you'r Silverlight project can’t contain a reference to anything but another Silverlight project. The best way around this is to have your model defined in a standard Class LIbrary Project, then create a Silverlight Class Library project and add each class file in your model to the Silverlight Class Library Project “As a Link”. This means there’s only one copy of each class file in your model, it’s a pain but for now it’s the only way to do it since you need access to your model definition in Silverlight and in your WCF project.

So in my “Interfaces” project, I’ve defined my interface and I have a linked copy of my Message class and this is the reference I use for module projects.

With these changes the only difference in my presenter class is the constructor arguments:

public HelloWorldPresenter(IHelloWorldView view, IMessageServiceClient service, IUnityContainer container)

I just had to change the type of my “service” argument.

This allows me to completely decouple the actual WCF client proxy implementation from my Silverlight application. But I had one more concern that bothered me at this point: does the use of Dispatcher mean I’m coupled to the UI? I didn’t know if there were a way to instantiate or mock Dispatcher so I could properly test my code.

As it turns out there is a dependency required here, but it really isn’t significant. I had to dig around in reflector to find it because I was looking to see how it was instantiated since it didn’t seem to have a public constructor. Mind you I haven’t started writing tests yet here, but if your project references the WindowsBase assembly then you can either use the Dispatcher.CurrentDispatcher static property or Dispatcher.FromThread() static method to get an instance of Dispatcher. From there you should be able to run your tests without any dependency on WCF or the UI.

For now, you can download a rough draft of the code from here. There’s still some cleanup required, but it should be good enough for the moment until I have time to finish it.

Labels: , , , ,