【问题标题】:Make wcf client wait for the callback让 wcf 客户端等待回调
【发布时间】:2017-03-01 07:47:32
【问题描述】:

我目前正在处理一个项目,我必须通过 wcf 客户端管理应用程序。我面临的问题是,在调用服务器后,我需要客户端等待回调。这是场景:

我调用显示窗口的服务,然后服务器应用程序处于空闲状态。当我单击窗口上的按钮时,它会回调客户端。在此期间,必须禁用客户端 UI - 它必须等待回调。您能告诉我如何实现这一目标吗?是不是跟并发模式或者Operation Contract属性有关系?

这是我的 ServiceContract 和 CallbackContract 代码:

[ServiceContract(CallbackContract = typeof(IWCFServiceCallback))]
public interface IWCFService
{
    [OperationContract]
    void OpenWindow();
}
public interface IWCFServiceCallback
{
    [OperationContract(IsOneWay = true)]
    void ReturnValue(object[] value);
}

【问题讨论】:

    标签: c# wcf


    【解决方案1】:

    不,您描述的功能与并发模式或操作合同无关。您可能需要使用信号量(MutexMonitor,等等)和从服务器到客户端的回调来设置信号量。

    话说你描述的功能看起来很奇怪。

    【讨论】:

      【解决方案2】:

      您可以通过实现异步服务操作并使用Async/Await 调用它来做到这一点。

      在调用您的服务之前禁用您的客户端 UI,然后在回调返回后启用它。

      https://msdn.microsoft.com/en-us/library/ms731177.aspx

      using System;
      using System.Collections.Generic;
      using System.ServiceModel;
      using System.Text;
      using System.Threading;
      
      namespace Microsoft.WCF.Documentation
      {
        [ServiceContractAttribute(Namespace="http://microsoft.wcf.documentation")]
        public interface ISampleService{
      
          [OperationContractAttribute]
          string SampleMethod(string msg);
      
          [OperationContractAttribute(AsyncPattern = true)]
          IAsyncResult BeginSampleMethod(string msg, AsyncCallback callback, object asyncState);
      
          //Note: There is no OperationContractAttribute for the end method.
          string EndSampleMethod(IAsyncResult result);
      
          [OperationContractAttribute(AsyncPattern=true)]
          IAsyncResult BeginServiceAsyncMethod(string msg, AsyncCallback callback, object asyncState);
      
          // Note: There is no OperationContractAttribute for the end method.
          string EndServiceAsyncMethod(IAsyncResult result);
        }
      
        public class SampleService : ISampleService
        {
          #region ISampleService Members
      
          public string  SampleMethod(string msg)
          {
            Console.WriteLine("Called synchronous sample method with \"{0}\"", msg);
            return "The sychronous service greets you: " + msg;
          }
      
          // This asynchronously implemented operation is never called because 
          // there is a synchronous version of the same method.
          public IAsyncResult BeginSampleMethod(string msg, AsyncCallback callback, object asyncState)
          {
            Console.WriteLine("BeginSampleMethod called with: " + msg);
            return new CompletedAsyncResult<string>(msg);
          }
      
          public string EndSampleMethod(IAsyncResult r)
          {
            CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
            Console.WriteLine("EndSampleMethod called with: " + result.Data);
            return result.Data;
          }
      
          public IAsyncResult BeginServiceAsyncMethod(string msg, AsyncCallback callback, object asyncState) 
          {
            Console.WriteLine("BeginServiceAsyncMethod called with: \"{0}\"", msg);
            return new CompletedAsyncResult<string>(msg);
          }
      
          public string EndServiceAsyncMethod(IAsyncResult r)
          {
            CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
            Console.WriteLine("EndServiceAsyncMethod called with: \"{0}\"", result.Data);
            return result.Data;
          }
          #endregion
        }
      
        // Simple async result implementation.
        class CompletedAsyncResult<T> : IAsyncResult
        {
          T data;
      
          public CompletedAsyncResult(T data)
          { this.data = data; }
      
          public T Data
          { get { return data; } }
      
          #region IAsyncResult Members
          public object AsyncState
          { get { return (object)data; } }
      
          public WaitHandle AsyncWaitHandle
          { get { throw new Exception("The method or operation is not implemented."); } }
      
          public bool CompletedSynchronously
          { get { return true; } }
      
          public bool IsCompleted
          { get { return true; } }
          #endregion
        }
      }
      

      【讨论】:

      • 感谢您的回复。我将补充一点,在调用一个方法之后,我希望客户端的行为与方法本身返回值(没有回调)一样 - 客户端等待服务完成其工作并返回一个值。我无法手动锁定 UI,因为客户端是 Excel 插件,它没有可以禁用的表单/窗口。
      • 在 Excel 打开 FunctionWizard 时调用服务,所以我不能只锁定单元格,因为它会使 Excel 崩溃。
      • @Bartek — 那是一个不同的问题,那么,对于单独的问答。我相信我已经提供了这个问题的答案,但如果没有让我知道,我会尽力提供进一步的帮助。仅供参考,微软在其示例代码中偏离了自己的recommended naming conventions。异步函数应在其名称后附加“Async”。当然,我们在这里看不到 async 关键字,但这仍然是一个异步架构。
      【解决方案3】:

      当并发模式为 ConcurrencyMode.Single 并且客户端调用服务时,服务将创建一个锁。当它调用 IsOneWay 为 false 的回调接口时,结果消息将被发送回服务。该服务将再次创建锁并将死锁,因为该锁仍然来自客户端调用。 使用 ConsurrencyMode。重入锁将在回调时偷偷释放并在返回时重新获取,以便您可以使用它。 IsOneWay=true 也是一种解决方案,因为没有结果消息被发送回服务回调。

      因此,您应该能够在调用服务之前锁定您的 GUI,并在回调操作具有 IsOneWay = true 或服务配置了 ConcurrencyMode.Reentrant 时在回调中解锁它

      【讨论】:

      • 问题是我无法手动锁定客户端的 UI,因为它是一个打开 FunctionWizard 的 Excel 插件。如果我尝试锁定单元格,Excel 会崩溃。
      【解决方案4】:

      感谢您的回答。我通过使用 Win32 ShowWindow 函数解决了这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-13
        • 1970-01-01
        • 2012-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多