Tutorial :Decoupling Silverlight client from service reference generated class



Question:

I am researching Prism v2 by going thru the quickstarts. And I have created a WCF service with the following signature:

namespace HelloWorld.Silverlight.Web  {  [ServiceContract(Namespace = "http://helloworld.org/messaging")]  [AspNetCompatibilityRequirements(RequirementsMode =                                   AspNetCompatibilityRequirementsMode.Allowed)]    public class HelloWorldMessageService    {      private string message = "Hello from WCF";        [OperationContract]      public void UpdateMessage(string message)      {        this.message = message;      }        [OperationContract]      public string GetMessage()      {        return message;      }    }  }  

When I add a service reference to this service in my silverlight project it generates an interface and a class:

[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);        string EndGetMessage(System.IAsyncResult result);  }    public partial class HelloWorldMessageServiceClient : System.ServiceModel.ClientBase<HelloWorld.Core.Web.Services.HelloWorldMessageService>, HelloWorld.Core.Web.Services.HelloWorldMessageService {  {      // implementation  }  

I'm trying to decouple my application by passing around the interface instead of the concrete class. But I'm having difficulty finding examples of how to do this. When I try and call EndGetMessage and then update my UI I get an exception about updating the UI on the wrong thread. How can I update the UI from a background thread?


I tried but I get UnauthorizedAccessException : Invalid cross-thread access.

string messageresult = _service.EndGetMessage(result);    Application.Current.RootVisual.Dispatcher.BeginInvoke(() => this.Message = messageresult );  

The exception is thrown by Application.Current.RootVisual.


Solution:1

Here is something I like doing... The service proxy is generated with an interface

HelloWorldClient : IHelloWorld  

But the problem is that IHelloWorld does not include the Async versions of the method. So, I create an async interface:

public interface IHelloWorldAsync : IHelloWorld  {      void HelloWorldAsync(...);      event System.EventHandler<HelloWorldEventRgs> HelloWorldCompleted;  }  

Then, you can tell the service proxy to implement the interface via partial:

public partial class HelloWorldClient : IHelloWorldAsync {}  

Because the HelloWorldClient does, indeed, implement those async methods, this works.

Then, I can just use IHelloWorldAsync everywhere and tell the UnityContainer to use HelloWorldClient for IHelloWorldAsync interfaces.


Solution:2

Ok, I have been messing with this all day and the solution is really much more simple than that. I originally wanted to call the methods on the interface instead of the concreate class. The interface generated by proxy class generator only includes the BeginXXX and EndXXX methods and I was getting an exception when I called EndXXX.

Well, I just finished reading up on System.Threading.Dispatcher and I finally understand how to use it. Dispatcher is a member of any class that inherits from DispatcherObject, which the UI elements do. The Dispatcher operates on the UI thread, which for most WPF applications there is only 1 UI thread. There are exceptions, but I believe you have to do this explicitly so you'll know if you're doing it. Otherwise, you've only got a single UI thread. So it is safe to store a reference to a Dispatcher for use in non-UI classes.

In my case I'm using Prism and my Presenter needs to update the UI (not directly, but it is firing IPropertyChanged.PropertyChanged events). So what I have done is in my Bootstrapper when I set the shell to Application.Current.RootVisual I also store a reference to the Dispatcher like this:

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>();            Application.Current.RootVisual = shell;            Container.RegisterInstance<Dispatcher>(shell.Dispatcher);            return shell;      }  }  

Then my presenter has a ctor which accepts IUnityContainer as an argument (using DI) then I can do the following:

_service.BeginGetMessage(new AsyncCallback(GetMessageAsyncComplete), null);        private void GetMessageAsyncComplete(IAsyncResult result)  {      string output = _service.EndGetMessage(result);      Dispatcher dispatcher = _container.Resolve<Dispatcher>();      dispatcher.BeginInvoke(() => this.Message = output);  }  

This is sooooo much simpler. I just didn't understand it before.


Solution:3

Ok, so my real problem was how to decouple my dependency upon the proxy class created by my service reference. I was trying to do that by using the interface generated along with the proxy class. Which could have worked fine, but then I would have also had to reference the project which owned the service reference and so it wouldn't be truly decoupled. So here's what I ended up doing. It's a bit of a hack, but it seems to be working, so far.

First here's my interface definition and an adapter class for the custom event handler args generated with my proxy:

using System.ComponentModel;    namespace HelloWorld.Interfaces.Services  {      public class GetMessageCompletedEventArgsAdapter : System.ComponentModel.AsyncCompletedEventArgs      {          private object[] results;            public GetMessageCompletedEventArgsAdapter(object[] results, System.Exception exception, bool cancelled, object userState) :              base(exception, cancelled, userState)          {              this.results = results;          }            public string Result          {              get              {                  base.RaiseExceptionIfNecessary();                  return ((string)(this.results[0]));              }          }      }        /// <summary>      /// Create a partial class file for the service reference (reference.cs) that assigns      /// this interface to the class - then you can use this reference instead of the      /// one that isn't working      /// </summary>        public interface IMessageServiceClient      {          event System.EventHandler<GetMessageCompletedEventArgsAdapter> GetMessageCompleted;          event System.EventHandler<AsyncCompletedEventArgs> UpdateMessageCompleted;            void GetMessageAsync();          void GetMessageAsync(object userState);            void UpdateMessageAsync(string message);          void UpdateMessageAsync(string message, object userState);      }  }  

Then I just needed to create a partial class which extends the proxy class generated by the service reference:

using System;    using HelloWorld.Interfaces.Services;  using System.Collections.Generic;    namespace HelloWorld.Core.Web.Services  {      public partial class HelloWorldMessageServiceClient : IMessageServiceClient      {            #region IMessageServiceClient Members            private event EventHandler<GetMessageCompletedEventArgsAdapter> handler;          private Dictionary<EventHandler<GetMessageCompletedEventArgsAdapter>, EventHandler<GetMessageCompletedEventArgs>> handlerDictionary               = new Dictionary<EventHandler<GetMessageCompletedEventArgsAdapter>, EventHandler<GetMessageCompletedEventArgs>>();            /// <remarks>          /// This is an adapter event which allows us to apply the IMessageServiceClient          /// interface to our MessageServiceClient. This way we can decouple our modules          /// from the implementation          /// </remarks>          event EventHandler<GetMessageCompletedEventArgsAdapter> IMessageServiceClient.GetMessageCompleted          {              add               {                   handler += value;                  EventHandler<GetMessageCompletedEventArgs> linkedhandler = new EventHandler<GetMessageCompletedEventArgs>(HelloWorldMessageServiceClient_GetMessageCompleted);                  this.GetMessageCompleted += linkedhandler;                  handlerDictionary.Add(value, linkedhandler);              }              remove               {                   handler -= value;                  EventHandler<GetMessageCompletedEventArgs> linkedhandler = handlerDictionary[value];                  this.GetMessageCompleted -= linkedhandler;                  handlerDictionary.Remove(value);              }          }            void HelloWorldMessageServiceClient_GetMessageCompleted(object sender, GetMessageCompletedEventArgs e)          {              if (this.handler == null)                  return;                this.handler(sender, new GetMessageCompletedEventArgsAdapter(new object[] { e.Result }, e.Error, e.Cancelled, e.UserState));          }            





        
Previous
Next Post »