如何在 wcf 服务中启用用户 GUI 响应以触发对客户端的双工回调

本文关键字:客户端 回调 响应 wcf 服务 GUI 用户 启用 | 更新日期: 2023-09-27 18:28:03

也许我正在尝试不可能的事情...我创建了一个 wpf 应用程序来使用以下服务协定启动 wcf 服务:

 [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IRejectCallback))]
public interface IRejectService
{
    [OperationContract(IsOneWay = true)]
    void SubmitNewRejectInfo();
    [OperationContract(IsOneWay = true)]
    void SendRejectCallback();
}

我的服务行为:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = true)]  //(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = true)]
public class RejectService : IRejectService, IPostRejectEvent

我创建了双工通道并调用 SubmitNewRejectInfo 服务方法:

    InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
    tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
    IRejectService _rejectService = tcpFactory.CreateChannel();
    _rejectService = _tcpFactory.CreateChannel();
    _rejectService.SubmitNewRejectInfo();

方法在服务端运行。通常,我会像这样添加我的回调方法:

    public void SubmitNewRejectInfo(RejectInformation rejectInformation)
    {
        // Do something here...
        callback.RejectCallback();
    }

但是,当从客户端运行 SubmitNewRejectInfo 方法(使用 IsOneWay = true(时,我不想在那个时候回调到客户端。 我想等待用户单击我的 WPF GUI 上的按钮,该按钮会将信号传输到回调到客户端。 ** 是否可以推迟回调,或通过其他操作合约发送回调?

客户端如何通过操作协定方法调用服务,然后在服务端发生用户交互后接收回调? 我看到一个双工示例,其中有人将可重入服务与 Thread.Sleep(( 一起使用,如下所示:

public void Register()
{
    OperationContext ctxt = OperationContext.Current;
    IClientCallback callBack = ctxt.GetCallbackChannel<IClientCallback>();
    Thread.Sleep(3000); 
    callBack.TempUpdate(10);
}

就我而言,在用户单击托管服务的 gui 上的按钮后,我需要在 Register 方法中触发回调。 这可能吗? 有什么想法吗?

更新**

发现了我的主要问题:我从 wcf 客户端调用我的操作服务协定方法:

        InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
        _tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
        _rejectService = _tcpFactory.CreateChannel();
        _rejectService.SubmitNewRejectInfo();

此处调用 wcf 服务操作:

        public void SubmitNewRejectInfo(RejectInformation rejectInformation)
        {
          // Throw event to notify MainViewModel that new reject information is available.
           OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
           callback.RejectCallback();
        }

触发该事件以通知我的 MainViewModel 某些数据已更新并刷新某些属性。 然后问题开始了... 我不想要回调。拒绝回调((;还没开火。 我需要用户单击与视图模型关联的主窗口 GUI 上的按钮,以"授权"双工回调向 wcf 客户端返回消息。

任何想法如何"暂停"回调足够长的时间,以便用户单击按钮以授权双工回调将消息传递给 wcf 客户端? 也许我的OnSubmitNewRejectInfo事件可以在调用回调之前返回一些事件参数? 在调用回调之前,是否可以触发新委托以从我的 MainViewModel 返回信息?

我希望这能更好地描述我的问题。 任何帮助都非常感谢。

更新编号 2 **更多信息... :)

WCF 服务是作为 WCF 服务类库创建的。WCF 客户端也是作为 WCF 服务类库创建的。这使其他应用程序或类对象可以轻松地承载服务和客户端。这样做是为了在服务端通过 GUI 进行人工交互,在 wcf 客户端进行其他软件交互。WCF 服务和客户端承载在不同的计算机上。

WCF 服务由 WPF 应用程序承载,通信在两者之间由事件驱动。服务类在 WPF 应用程序的 MainViewModel 中作为单一实例创建。

WCF 服务类必须通过与 WCF 客户端的双工通信进行通信。客户端调用操作协定来更新服务中的信息,这些信息显示在 WPF GUI 上。在 GUI 上显示信息后,用户必须单击按钮以调用对客户端的回调,指示服务已完成其任务。

因此,WPF 应用承载 wpf 服务类库。WPF 应用和服务类之间通过事件进行通信。服务类由 wcf 客户端通过双工通道通信使用。wcf 客户端还由另一个类对象承载,该类对象具有对 wcf 服务的服务引用。客户端通过事件与其主机通信。

WCF 客户端代码:

        InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
        _tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
        _rejectService = _tcpFactory.CreateChannel();
        _rejectService.SubmitNewRejectInfo(); // This is where I invoke a service operation from my client.

WCF 服务代码:

        // This service operation is consumed by the client.
        public void SubmitNewRejectInfo(RejectInformation rejectInformation)
        {
          // Create event to notify MainViewModel that new reject info is available.
           OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
           // **** I need something to happen here in order to halt the duplex callback to the client until a human creates a button click event in my MainViewModel, which indicates the duplex callback may be sent back to the client. ****
           callback.RejectCallback();
        }

对不起,这个问题已经变得非常详细。我永远不应该在大学的技术写作课上睡着......:)

更新编号 3 **

我尝试运行下面提到的代码。 他的示例代码非常适合我的需求!!(谢谢德戈罗尔!但是,我得到一个空引用异常:"对象引用未设置为对象的实例"。

首先,该操作在 degoroll 演示代码的这一部分中执行:

if (pendingNotifications.TryGetValue(rejectInformation, out action(( { 尝试 { 操作(拒绝信息(; 这是被调用的

然后这部分演示代码称为 ->回调。RejectCallback(new RejectCallbackMessage((((;:

    public void SubmitNewRejectInfo(RejectInformation rejectInformation)
    {
        // Throw event to notify MainViewModel that new reject information is available.
        OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));

        pendingNotifications.Add(rejectInformation, info => callback.RejectCallback(new RejectCallbackMessage()));   // **** the action returns to callback.RejectCallback here ****

这是我得到空异常错误的地方。

这是我获取回调通道的代码:

    IRejectCallback callback
    {
        get { return OperationContext.Current.GetCallbackChannel<IRejectCallback>(); }
    }

我的猜测是我没有返回 null 而不是原始回调通道......

有没有办法在代码的这一点上获得正确的通道?

如何在 wcf 服务中启用用户 GUI 响应以触发对客户端的双工回调

如果我理解正确,似乎服务器只需要保留它正在等待做的事情的列表。实现方式将与服务器的实例化密切相关。如果您坚持使用单例,则只需在服务器类中保存待处理通知的映射即可。例如:

public class RejectService
{
    Dictionary<RejectInformation, Action<RejectInformation>> pendingNotifications = new Dictionary<RejectInformation, Action<RejectInformation>>();
    public void SubmitNewRejectInfo(RejectInformation rejectInformation)
    {
       OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
       pendingNotifications.Add(rejectInformation, info => callback.RejectCallback(info));
    }
    public void SendRejectCallback(RejectInformation rejectInformation)
    {
       Action<RejectInformation> action;
       if (pendingNotifications.TryGetValue(rejectInformation, out action))
       {
          acion(rejectInformation);
          pendingNotifications.Remove(rejectInformation);
       }
    }
}

如果你想让这个重入者,你可能需要考虑锁......这是一种非常简单的方法,但给出了一个起点。