2009-03-06 38 views
4

我正在通过快速入门来研究Prism v2。我已创建了以下签名的WCF服务:从服务引用生成的类中解耦Silverlight客户端

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

当我在我的Silverlight项目添加服务引用该服务将生成一个接口和一个类:

[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 
} 

我试图通过传递接口而不是具体类来解耦我的应用程序。但我很难找到如何做到这一点的例子。当我尝试调用EndGetMessage然后更新我的UI时,我得到了关于在错误线程上更新UI的异常。我如何从后台线程更新UI?


我试过但我得到UnauthorizedAccessException : Invalid cross-thread access

string messageresult = _service.EndGetMessage(result); 

Application.Current.RootVisual.Dispatcher.BeginInvoke(() => this.Message = messageresult); 

Application.Current.RootVisual抛出异常。

回答

0

传递接口(一旦你实例化客户端)应该像使用HelloWorldMessageService而不是HelloWorldMessageServiceClient类一样简单。

为了更新UI,您需要使用Dispatcher对象。这使您可以提供在UI线程的上下文中调用的委托。有关详细信息,请参阅此blog post

+0

我试过,但我得到UnauthorizedAccessException:无效的跨线程访问。 string messageresult = _service.EndGetMessage(result); Application.Current.RootVisual.Dispatcher.BeginInvoke(()=> this.Message = messageresult ); Application.Current.RootVisual抛出异常 – 2009-03-06 01:20:12

1

好吧,所以我真正的问题是如何分离我的依赖于由我的服务引用创建的代理类。我试图通过使用与代理类一起生成的接口来实现。这可能工作得很好,但是之后我也必须引用拥有服务引用的项目,因此它不会真正解耦。所以这就是我最终做的。这有点破解,但到目前为止它似乎还在工作。

首先这里是我的接口定义,并与我的代理产生的自定义事件处理程序ARGS适配器类:

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

然后我需要创建延伸服务引用生成的代理类的分部类:

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

这是一个显式实现的事件处理程序,所以我可以将事件链接在一起。当用户注册我的适配器事件时,我注册了实际的事件。当事件发生时,我会触发我的适配器事件。到目前为止,这个“在我的机器上工作”。

1

好的,我一整天都在搞这个,解决方案真的比这个简单得多。我原本想调用接口上的方法而不是concreate类。由代理类生成器生成的接口只包含BeginXXXEndXXX方法,我在调用EndXXX时遇到了异常。

那么,我刚刚读完System.Threading.Dispatcher,我终于明白如何使用它了。 Dispatcher是从UI元素所做的DispatcherObject继承的任何类的成员。 Dispatcher在UI线程上运行,对于大多数WPF应用程序,只有1个UI线程。有例外,但我相信你必须明确地做到这一点,所以你会知道你是否在做。否则,你只有一个UI线程。因此,存储对Dispatcher的引用以便在非UI类中使用是安全的。

在我的情况下,我使用棱镜和我的演示者需要更新UI(不直接,但它正在发射IPropertyChanged.PropertyChanged事件)。所以我做了什么,在我Bootstrapper当我设置壳Application.Current.RootVisual我还可以存储到Dispatcher这样的引用:

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

然后我的演讲具有接受IUnityContainer作为参数构造函数(使用DI)那么我可以做到以下几点:

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

这太简单了。我以前不明白。

0

你可以使这个更简单。

代理工作的原因和您的合同副本不是因为WCF生成的代理使用代码在调用线程上“回传”回调,而不是在服务执行时执行的线程上进行回调致电退货。

一个更简化的,未经检验的,部分实现,给你的WCF代理工作怎么看起来像的想法:

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

的关键是syncronizationContext的存储和调用Post方法。这将使得回调在与Begin被调用相同的线程上发生。只要您从UI线程调用Begin,它将始终工作而不涉及Dispatcher对象。如果你不这样做,那么你使用Dispatcher回到原来的状态,但是WCF代理也会出现同样的问题。

这个环节做了解释的一个好工作如何手动做到这一点:
http://msdn.microsoft.com/en-us/library/dd744834(VS.95).aspx

2

这是我喜欢做......服务代理与接口生成

HelloWorldClient : IHelloWorld 

但问题是IHelloWorld不包含该方法的异步版本。所以,我创建一个异步接口:

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

然后,你可以告诉服务代理通过局部实现接口:

public partial class HelloWorldClient : IHelloWorldAsync {} 

因为HelloWorldClient确实,确实,实现这些异步方法,这作品。

然后,我可以随处使用IHelloWorldAsync,并告诉UnityContainer使用HelloWorldClient代替IHelloWorldAsync接口。

+0

我喜欢这个,因为你可以让你的mock类的HelloWorldAsync方法调用HelloWorldCompleted函数,如果不是null,例如, HelloWorldCompleted(null,new HelloWorldEventArgs(“hello world”,null,false,null))。我在Silverlight单元测试中尝试了这一点,它可以工作。不要搞乱Async单元测试,不需要调度你的RaisePropertyChanged通知。你只是想知道SL/WCF为什么不自动在服务接口上包含基于事件的方法。它可以节省很多代码! – 2011-12-02 18:44:24

相关问题