views:

1711

answers:

7

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?

A: 

Passing around the interface (once you have instantiated the client) should be as simply as using HelloWorldMessageService instead of the HelloWorldMessageServiceClient class.

In order to update the UI you need to use the Dispatcher object. This lets you provide a delegate that is invoked in the context of the UI thread. See this blog post for some details.

Rob Walker
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
Mark J Miller
A: 

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

Mark J Miller
Where did you call BeginGetMessage from?
Rob Walker
From my presentation model
Mark J Miller
+1  A: 

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));
 }

 #endregion
}

}

This is an explicit implementation of the event handler so I can chain together the events. When user registers for my adapter event, I register for the actual event fired. When the event fires I fire my adapter event. So far this "Works On My Machine".

Mark J Miller
+1  A: 

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.BeginInvoke(() => this.Message = output); }

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

Mark J Miller
A: 

You can make this much simpler still.

The reason the proxy works and your copy of the contract does not is because WCF generates the proxy with code that "Posts" the callback back on the calling thread rather than making the callback on the thread that is executing when the service call returns.

A much simplified, untested, partial implementation to give you the idea of how WCF proxies work looks something like:

{
    var state = new
        {
            CallingThread = SynchronizationContext.Current,
            Callback = yourCallback
            EndYourMethod = // assign delegate
        };

    yourService.BeginYourMethod(yourParams, WcfCallback, state);
}

private void WcfCallback(IAsyncResult asyncResult)
{
    // Read the result object data to get state
    // Call EndYourMethod and block until the finished
    state.Context.Post(state.YourCallback, endYourMethodResultValue);
}

The key is the storing of the syncronizationContext and calling the Post method. This will get the callback to occur on the same thread as Begin was called on. It will always work without involving the Dispatcher object provided you call Begin from your UI thread. If you don't then you are back to square one with using the Dispatcher, but the same problem will occur with a WCF proxy.

This link does a good job of explaining how to do this manually:
http://msdn.microsoft.com/en-us/library/dd744834(VS.95).aspx

+1  A: 

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.

Brian Genisio
A: 

Just revisiting old posts left unanswered where I finally found an answer. Here's a post I recently wrote that goes into detail about how I finally handled all this:

http://www.developmentalmadness.com/archive/2009/11/04/mvvm-with-prism-101-ndash-part-6-commands.aspx

Mark J Miller