有没有一种简单的方法来模拟 WCF 服务和 WCF 客户端之间的事件?

Posted

技术标签:

【中文标题】有没有一种简单的方法来模拟 WCF 服务和 WCF 客户端之间的事件?【英文标题】:Is there an easy way to simulate events between WCF service and WCF client? 【发布时间】:2017-10-14 00:20:53 【问题描述】:

所以我已经实现了客户端 EXE 和服务器 EXE 之间的 localhost WCF 命名管道通信。我可以通过本地主机调用服务器上的类方法。所以,它就像一个 IPC/RPC。但是,如果服务器的类方法需要很长时间才能执行,那么我最好把它扔到一个线程中,以便服务器类方法完成并在后台运行这个线程。好的,很好,但是当线程完成它的长任务时,我想提醒客户端,而不必在客户端上使用会检查该类方法的计时器。击中类方法的计时器比引发的事件效率低得多。就像我需要从服务器在客户端上引发一个事件。有没有一种简单的方法可以做到这一点,或者至少可以模拟它,而不需要很多令人困惑的工作?

【问题讨论】:

你可以让你的 WCF 方法 asynchonous 然后这是一个简单的问题 async/await 或者完全取消 WCF 并使用内置的异步与 NamedPipeClientStream (它仍然与 await 兼容)。更不用说后者在消除冗长的 XML SOAP 编码时的速度提升 @MickyD 你在异步/等待的事情上是对的,因为我已经研究了它并实施了一个有效的测试。这让我几乎可以用最少的代码行模拟一个长时间运行的任务的回调。 【参考方案1】:

这是根据我对 OP 问题的评论得出的答案


您可以使您的 WCF 方法 asynchonous 然后这是一个简单的问题 async/await 或完全取消 WCF 并使用带有 NamedPipeClientStream 的内置异步(仍然与等待兼容)。更不用说后者在取消冗长的 XML SOAP 编码时的速度提升

操作:

@MickyD 你在 async/await 问题上是对的,因为我已经研究过它并实施了一个有效的测试。这让我几乎可以用最少的代码行模拟一个长时间运行的任务的回调

例如以OP的答案为基础,但要正确使用async/await

客户端代码

private async void button1_Click(object sender, EventArgs e) // <-- note async

    label1.Text = await client.GetDataAsync(textBox1.Text); // <-- note await. New method

现在您可能会想使用Task.Run,但这样做很糟糕,因为:

    Task.Run 最适合我们不是的计算密集型操作。 Task.Run 最多会使用一个昂贵的线程池线程

我们正在执行 I/O 操作,因此可以受益于 I/O 完成端口和 “没有线程” IOCP 哲学存在于任务 I/O 绑定操作中。因此,当我们通过GetDataAsync 进行服务器调用时,我们不会浪费线程等待结果。

WCF 服务器

这里我们通过等待来模拟一个冗长的操作,但不是使用Sleep(不是Task-aware),而是使用Task.Delay,这是一个等待操作。

Task<string> async GetDataAsync (string text)

    await Task.Delay (Timespan.FromSeconds(5));
    return text + " processed";

【讨论】:

我为什么不用在 WCF 服务 EXE 中实现 Task&lt;string&gt; async GetDataAsync(string test) 就可以让它工作? 微软好像自动为你创建了*Async(*)类方法。我创建了一个Example2(),然后在客户端做了更新服务参考,然后就可以自动调用Example2Async()了。 @Volomike 是的,没错。您可能想看看 WCF the Manual Way… the Right Way 因为它可以防止客户端代理不同意实际服务的问题。 :)【参考方案2】:

假设您在 UI 中有一个按钮单击,它对 WCF 服务执行 WCF 同步方法调用,并且该同步方法需要很长时间才能运行。显然,您不想在长时间运行的任务执行时阻止 UI 更新。当然,您可能正在考虑回调。例如,您调用服务器,服务器上的类方法会生成一个线程并运行该任务,完成后,它会通过回调将结果返回给客户端。

实际上,在 WCF 上设置所有这些涉及到许多复杂、令人困惑、记录不充分的步骤。但是有一个更简单的方法,它根本不涉及 WCF 代码,也不涉及您更改 WCF 服务上的任何内容,也不涉及编辑任何 WCF 配置。该技巧在 .NET 4.5 及更高版本中引入。它被称为asyncawait。这是一个按钮单击示例,它调用了一个 WCF 服务方法,该方法需要很长时间才能运行,然后在完成后返回结果,但 GUI 不会锁定并可以处理其他事件。

1。首先,要模拟一个慢速任务,编辑你的 WCF 服务项目的共享类方法,并在返回结果之前添加这一行,这样你就可以模拟 5 秒的暂停:

Thread.Sleep(5000); // requires using System.Threading;

就我而言,我把它放在我的 GetData() 方法中。

2。现在切换到您的 WCF 客户端项目。例如,您可能有一个如下所示的按钮单击处理程序:

private void button1_Click(object sender, EventArgs e)

  string returnString = client.GetData(textBox1.Text));
  label1.Text = returnString;

所以,用三个小改动来切换它:

一个。添加using System.Threading.Tasks;

b.在您的按钮单击处理程序上将 private void... 更改为 private async void...

c。在您的慢速方法调用中使用await Task.Run(...)

因此,代码如下所示:

private async void button1_Click(object sender, EventArgs e)

  // Task.Run() requires "using System.Threading.Tasks;"
  string returnString = await Task.Run(() => client.GetData(textBox1.Text));
  label1.Text = returnString;

最终的结果是,当你点击 WCF 客户端项目中的按钮时,在后台线程中对 WCF 服务项目调用 GetData() 类方法,完成后又返回到那个await语句并将结果返回给变量赋值。就我而言,我单击了按钮,但 5 秒内什么也没发生——结果字符串的标签没有改变。然而,GUI 并没有被锁定——我可以拖动窗口、输入其他字段、单击其他表单按钮等等。所以,它几乎就像一个回调事件处理程序,但不完全是。尽管如此,它仍然提供相同的功能,并且在大多数情况下可以用来代替回调事件处理程序。而且它涉及的代码要少得多。

【讨论】:

以上是关于有没有一种简单的方法来模拟 WCF 服务和 WCF 客户端之间的事件?的主要内容,如果未能解决你的问题,请参考以下文章

为 WCF REST 服务生成示例数据?

JDE WCF 服务

WCF 模拟和 SQL 可信连接?

WCF 服务中的模拟和传播到 COM 对象

WCF学习——构建一个简单的WCF应用

WCF Windows 服务数据库连接和模拟问题