【问题标题】:How to notify a client from wf4-wcf-server asynchronously?如何从 wf4-wcf-server 异步通知客户端?
【发布时间】:2012-08-09 07:09:31
【问题描述】:

在过去的几天里,我花了很多时间寻找如何将简单消息从工作流 (WF4) 服务器发送到客户端(在我的情况下为 WPF)的解决方案。

存在一些如何回复请求(通过回调)的示例,但我找不到任何如何异步联系客户端的示例。

我希望能够进行这样的交流:

  • [上午 8:00] 客户:“您好服务器,这是我的请求。请使用此配方启动您的工作流程 'MakeCoffee'。”。
  • [8:01 am] WF-Server:“您好,客户端,我正在处理。”
  • [8:05 am] WF-Server:“我找不到咖啡。我要买一些。”
  • [8:10 am] WF-Server:“我在烧水。”
  • [8:15 am] WF-Server:“你想要糖吗?”
  • [上午 8 点 17 分] 客户:“是的。”
  • [8:18 am] WF-Server:“我完成了。”

到目前为止,我找到了以下解决方案:

1。 WCF - 带有回调通道的服务

This example 似乎很酷,但我不知道如何在我的 Workflow-Service 中使用它——因为没有“实现服务”类。一切都是可视的,所以我不能应用“SubscribeClient”方法(或者至少我不知道在哪里放置服务实现)。

2。带有通知事件的工作流

This example 是一个不错的解决方案 - 但(据我了解)这不适用于单独的工作流服务器 - 它似乎要求工作流服务器和客户端相同。

3。访问 OperationContext

This example 在客户端创建了一个 WorkflowServiceHost,但处理起来并不容易,我无法根据自己的要求进行调整。

有人知道如何将消息从(单独的)工作流服务器发送到客户端的便捷解决方案/模式吗? 客户端不知道服务器何时可以向他发送消息以及如何发送消息很多。

我是 WF4 的新手,非常感谢每一个提示/建议/想法。

提前致谢,

提莫

更新

根据 Maurice 的评论,我尝试在我的客户端中使用简单的服务,但不幸的是,我的客户端没有收到并显示任何消息。

这是我的客户代码:

public partial class MainWindow : Window  
{  
    private ServiceHost _serviceHost;  
    private void Window_Loaded(object sender, RoutedEventArgs e)  
    { 
        ApplicationInterface._app = this;  
        //prepare the serviceHost  
        string clientAddress = "http://localhost:8000/ClientService";  
        System.ServiceModel.Channels.Binding bBinding = new BasicHttpBinding();  
        _serviceHost.AddServiceEndpoint(typeof(IClientService), bBinding, clientAddress);  
        _serviceHost.Open();  
    }

    public ListBox GetEventListBox()
    {
        return this.lstEvents;
    }
}  

这是我的 IClientService 接口:

[ServiceContract]  
public interface IClientService  
{  
    [OperationContract(IsOneWay=true)]  
    void MeldeStatus(String statusText);  
}  

这是 IClientService-Interface 的实现:

public class ClientService : IClientService  
{  
    public void MeldeStatus(string statusText)  
    {  
        ApplicationInterface.AddEvent(statusText);  
    }  
}  

这是静态应用程序接口:

public static class ApplicationInterface  
{  
    public static MainWindow _app { get; set; }  

    public static void AddEvent(String eventText)  
    {  
        if (_app != null)  
        {  
            new ListBoxTextWriter(_app.GetEventListBox()).WriteLine(eventText);  
        }  
    }  
}

这是 ListBoxTextWriter 类,它应该将消息添加到给定的 ListBox:

public class ListBoxTextWriter : TextWriter
{
    const string textClosed = "This TextWriter must be opened before use";

    private Encoding _encoding;
    private bool _isOpen = false;
    private ListBox _listBox;

    public ListBoxTextWriter()
    {
        // Get the static list box
        _listBox = ApplicationInterface._app.GetEventListBox();
        if (_listBox != null)
            _isOpen = true;
    }

    public ListBoxTextWriter(ListBox listBox)
    {
        this._listBox = listBox;
        this._isOpen = true;
    }

    public override Encoding Encoding
    {
        get
        {
            if (_encoding == null)
            {
                _encoding = new UnicodeEncoding(false, false);
            }
            return _encoding;
        }
    }

    public override void Close()
    {
        this.Dispose(true);
    }

    protected override void Dispose(bool disposing)
    {
        this._isOpen = false;
        base.Dispose(disposing);
    }

    public override string ToString()
    {
        return "";
    }

    public override void Write(char value)
    {
        if (!this._isOpen)
            throw new ApplicationException(textClosed); ;

        this._listBox.Dispatcher.BeginInvoke
            (new Action(() => this._listBox.Items.Add(value.ToString())));
    }

    public override void Write(string value)
    {
        if (!this._isOpen)
            throw new ApplicationException(textClosed); ;

        if (value != null)
            this._listBox.Dispatcher.BeginInvoke
                (new Action(() => this._listBox.Items.Add(value)));
    }

    public override void Write(char[] buffer, int index, int count)
    {
        String toAdd = "";

        if (!this._isOpen)
            throw new ApplicationException(textClosed); ;

        if (buffer == null || index < 0 || count < 0)
            throw new ArgumentOutOfRangeException("buffer");


        if ((buffer.Length - index) < count)
            throw new ArgumentException("The buffer is too small");

        for (int i = 0; i < count; i++)
            toAdd += buffer[i];

        this._listBox.Dispatcher.BeginInvoke
            (new Action(() => this._listBox.Items.Add(toAdd)));
    }
}

我的 WF-Server 使用此发送活动:

<Send Action="MeldeStatus" EndpointConfigurationName="BasicHttpBinding_Client" sap:VirtualizedContainerService.HintSize="255,90" OperationName="MeldeStatus" ProtectionLevel="None" ServiceContractName="p:IClientService">
  <SendParametersContent>
    <p1:InArgument x:TypeArguments="x:String" x:Key="statusText">Yes, it works.</p1:InArgument>
  </SendParametersContent>
</Send>

这是我在 WF 服务器上的端点的配置:

<system.serviceModel>
  <bindings>
    <basicHttpBinding>        
      <binding name="DefaultHTTPBinding" allowCookies="true" />
    </basicHttpBinding>
  </bindings>    
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true"/>          
        <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>    
  <services>
    <service name="WFServer.ReklamationErfassen">
      <endpoint address="http://localhost:7812/ReklamationErfassen.xamlx"
                binding="basicHttpBinding"
                bindingConfiguration="DefaultHTTPBinding"
                contract="IReklamationService" />
    </service>
  </services>
  <client>      
      <endpoint address="http://localhost:8000/ClientService" binding="basicHttpBinding"
          bindingConfiguration="DefaultHTTPBinding" contract="IClientService"
          name="BasicHttpBinding_Client" />
  </client>
</system.serviceModel>
<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

我没有收到任何消息、错误或警告 - 有人可以快速检查给定的代码吗?

再次感谢,
蒂莫

【问题讨论】:

  • 很难看出发生了什么。我建议添加跟踪和 Fiddler 以查看消息是否实际发送并且未在客户端上处理,或者甚至没有从工作流发送。然后根据您的发现进行进一步调查。

标签: c# wcf asynchronous workflow-foundation-4 wcf-client


【解决方案1】:

有很多方法可以做到这一点,但其中两种最简单的是:

  • 使用 Send 活动将 SOAP 消息发送到 WCF 终结点,以供托管的客户端应用程序使用。如果客户端需要响应,只需添加 Receive 活动以等待响应。
  • 使用数据库表将消息写入其中。客户端应用程序轮询消息并将这些消息显示给用户。如果需要回复,您可以使用回复恢复自定义活动的书签。

第一个取决于 WCF 和在 WorkflowServiceHost 中托管的响应。第二种方法更通用,不依赖于 WCF,但需要更多代码和自定义活动。

【讨论】:

  • 谢谢你,莫里斯。我尝试了您的解决方案(之前),但没有奏效。我更新了我的问题并发布了我在一个最小示例中使用的代码。您能否(或其他)请快速查看代码?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 2013-08-24
相关资源
最近更新 更多