前言:

  • 在上一篇WCF初探-10:WCF客户端调用服务 中,我详细介绍了WCF客户端调用服务的方法,但是,这些操作都是同步进行的。有时我们需要长时间处理应用程序并得到返回结果,但又不想影响程序后面代码部分的执行,这时我们就需要考虑使用异步的方式来调用服务。注意这里的异步是完全针对客户端而言的,与WCF服务契约的方法是否异步无关,也就是在不改变操作契约的情况下,我们可以用同步或者异步的方式调用WCF服务。

WCF客户端异步调用服务方式:

  • 通过代理类异步调用服务。就需要通过使用事件驱动的异步调用模型,客户端可以对此接口异步调用操作。基于事件的异步模型设计准则规定,如果返回了多个值,则一个值会作为 Result 属性返回,其他值会作为 EventArgs 对象上的属性返回。 因此产生的结果之一是,如果客户端使用基于事件的异步命令选项导入元数据,且该操作返回多个值,则默认的 EventArgs 对象返回一个值作为 Result 属性,返回的其余值是 EventArgs 对象的属性。如果要将消息对象作为 Result 属性来接收并要使返回的值作为该对象上的属性,请使用 /messageContract 命令选项。 这会生成一个签名,该签名会将响应消息作为 EventArgs 对象上的 Result 属性返回。 然后,所有内部返回值就都是响应消息对象的属性了。
  • 使用通道工厂以异步方式调用操作。使用 ChannelFactory<TChannel> 时,不支持事件驱动的异步调用模型。这时我们需要异步重写服务契约的接口,每个方法都包含一组beginXXX和endXXX,XXX代表方法名。并且我们需要将异步操作的方法上的 AsyncPattern 属性设置为 true。前一个方法启动调用,而后一个方法在操作完成时检索结果。

WCF客户端异步调用服务示例:

  • 工程结构如下图所示:

  

  • 工程结构说明:
  1. Service:类库程序,定义服务契约和实现,里面包含User数据契约和GetInfo()获取用户信息的服务契约方法。

  IUserInfo.cs的代码如下:

  

using System.ServiceModel;
using System.Collections.Generic;
using System.Runtime.Serialization; namespace Service
{
[ServiceContract]
public interface IUserInfo
{
[OperationContract]
User[] GetInfo(int? id=null);
} [DataContract]
public class User
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public string Nationality { get; set; }
}
}

  UserInfo.cs的代码如下:

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace Service
{
public class UserInfo:IUserInfo
{
public User[] GetInfo(int? id=null)
{
Thread.Sleep(); List<User> Users = new List<User>();
Users.Add(new User { ID = , Name = "JACK", Age = , Nationality = "CHINA" });
Users.Add(new User { ID = , Name = "TOM", Age = , Nationality = "JAPAN" });
Users.Add(new User { ID = , Name = "SMITH", Age = , Nationality = "KOREA" });
Users.Add(new User { ID = , Name = "ALENCE", Age = , Nationality = "INDIA" });
Users.Add(new User { ID = , Name = "JOHN", Age = , Nationality = "SINGAPORE" }); if (id != null)
{
return Users.Where(x => x.ID == id).ToArray();
}
else
{
return Users.ToArray();
}
}
}
}

  2.  Host:控制台应用程序,添加对Service程序集的引用,寄宿服务程序。

  Program.cs代码如下

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel; namespace Host
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(UserInfo)))
{
host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
host.Open();
Console.Read();
}
}
}
}

  App.config代码如下:

  

<?xml version="1.0"?>
<configuration>
<system.serviceModel> <services>
<service name="Service.UserInfo" behaviorConfiguration="mexBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:1234/UserInfo/"/>
</baseAddresses>
</host>
<endpoint address="" binding="wsHttpBinding" contract="Service.IUserInfo" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services> <behaviors>
<serviceBehaviors>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

  3. Client1:控制台应用程序。添加对服务终结点地址http://localhost:1234/UserInfo/的引用,设置服务命名空间为UserInfoServiceRef,点击高级设置,勾选生成异步操作选项,

    生成客户端代理类和配置文件代码后,完成Client1对服务的调用。

  

  

  Program.cs的代码如下: 

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client1.UserInfoServiceRef; namespace Client1
{
class Program
{
static void Main(string[] args)
{
UserInfoClient proxy = new UserInfoClient();
proxy.GetInfoCompleted += new EventHandler<GetInfoCompletedEventArgs>(proxy_GetInfoCompleted);
proxy.GetInfoAsync(null);
Console.WriteLine("此字符串在调用方法前输出,说明异步调用成功!");
Console.Read();
} static void proxy_GetInfoCompleted(object sender, GetInfoCompletedEventArgs e)
{
User[] Users = e.Result.ToArray();
Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality");
for (int i = ; i < Users.Length; i++)
{
Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}",
Users[i].ID.ToString(),
Users[i].Name.ToString(),
Users[i].Age.ToString(),
Users[i].Nationality.ToString());
}
}
}
}

  从上面的代码可以看出客户端代理类调用采用了事件驱动机制,服务的方法GetInfo()与基于事件的异步调用方法一起使用且形式为GetInfoCompleted 的操作完成事件。客户端具体实现在proxy_GetInfoCompleted事件里,通过参数类型GetInfoCompletedEventArgsResult可以获得返回结果。而proxy.GetInfoAsync(null)代码说明服务开始异步调用。在此客户端我特地输出了Console.WriteLine("此字符串在调用方法前输出,说明异步调用成功!")一串文字来证明服务是异步调用的。因为在操作契约GetInfo()的方法中,我让程序线程休眠了1s,模拟程序执行的时间。如果客户端调用服务异步执行,我们应该会看到字符串文字应该显示在调用结果的前面。程序结果显示如下,说明结果调用成功。

  

  4. Client2: 控制台应用程序。此客户端我们是通过svcutil.exe工具生成的客户端代理类。在命令行中输入以下图中的命令,将生成的UserInfoClient.cs和App.config

    复制到Client2的工程目录下,Program.cs的代码实现和Client1中的一样。

  

  5. Client3: 控制台应用程序。在此客户端中,我们将通过ChannelFactory<TChannel>的方式对服务进行异步调用。想一想:我们如果对服务契约进行程序集引用,

    可是我们的服务契约并没有异步服务方法的定义,那我们怎么来调用服务方法呢?所以使用ChannelFactory<TChannel>的方式对服务进行异步调用就要比同步的

    方式多一个步骤,那就是我们必须在客户端对服务契约的方法进行重写,而不是对服务契约程序集的引用,就像代理方式一样。代码参考如下:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="IUserInfo")]
public interface IUserInfo
{ [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IUserInfo/GetInfo", ReplyAction="http://tempuri.org/IUserInfo/GetInfoResponse")]
Service.User[] GetInfo(System.Nullable<int> id); [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IUserInfo/GetInfo", ReplyAction="http://tempuri.org/IUserInfo/GetInfoResponse")]
System.IAsyncResult BeginGetInfo(System.Nullable<int> id, System.AsyncCallback callback, object asyncState); Service.User[] EndGetInfo(System.IAsyncResult result);
}

   这样我可以对服务的操作方法进行异步调用了,但是ChannelFactory<TChannel>的方式不支持事件驱动模型,所以我们可以利用回调函数来对其服务方法进行调用。

   接下来,我们完成Client3的代码操作。

     第一步:利用svcutil.exe生成客户端类,我们输入一下命令,将生成的文件复制到Client3的工程目录下。

   

   第二步:实现Client3的Program.cs的代码。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using Service; namespace Client3
{
class Program
{
static void Main(string[] args)
{
EndpointAddress address = new EndpointAddress("http://localhost:1234/UserInfo");
WSHttpBinding binding = new WSHttpBinding();
ChannelFactory<IUserInfo> factory = new ChannelFactory<IUserInfo>(binding, address);
IUserInfo channel = factory.CreateChannel();
IAsyncResult ar = channel.BeginGetInfo(null, GetInfoCallback, channel);
Console.WriteLine("此字符串在调用方法前输出,说明异步调用成功!"); Console.Read();
} static void GetInfoCallback(IAsyncResult ar)
{
IUserInfo m_service = ar.AsyncState as IUserInfo;
User[] Users = m_service.EndGetInfo(ar); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality");
for (int i = ; i < Users.Length; i++)
{
Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}",
Users[i].ID.ToString(),
Users[i].Name.ToString(),
Users[i].Age.ToString(),
Users[i].Nationality.ToString());
}
}
}
}
  • 总结:我们可以看到客户端异步调用服务的方法和同步的差不多,只是实现的机制不一样。如果读者想要更好的理解代码,我建议读者去了解一下IAsyncResult,关于这一部分内容,我将在以后的博文中做解读。

WCF初探-11:WCF客户端异步调用服务的更多相关文章

  1. WCF系列教程之客户端异步调用服务

    本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一.简介 在前面的随笔中,详细的介绍了WCF客户端服务的调用方法 ...

  2. 通过.NET客户端异步调用Web API(C#)

    在学习Web API的基础课程 Calling a Web API From a .NET Client (C#) 中,作者介绍了如何客户端调用WEB API,并给了示例代码. 但是,那些代码并不是非 ...

  3. Webservice客户端动态调用服务端功能方法

    一.发布WebService服务 方式一:在服务端生成wsdl文件,下方客户端直接引用即可     优点:针对要发布的方法生成一个wsdl文件即可,无需多余配置.   缺点:每次服务端方法发生改变都需 ...

  4. cxf 生成客户端代码调用服务

    cxf是另一种发布webservice的方式,与jdk提供的相比 jdk提供的是wsimport cxf 提供的是 wsdl2java- d 地址 根据http://www.cnblogs.com/f ...

  5. WCF初探文章列表

    WCF初探-1:认识WCF WCF初探-6:WCF服务配置 WCF初探-2:手动实现WCF程序 WCF初探-7:WCF服务配置工具使用 WCF初探-3:WCF消息交换模式之单向模式 WCF初探-8:W ...

  6. WCF初探-15:WCF操作协定

    前言: 在前面的文章中,我们定义服务协定时,在它的操作方法上都会加上OperationContract特性,此特性属于OperationContractAttribute 类,将OperationCo ...

  7. WCF初探-10:WCF客户端调用服务

    创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: ...

  8. 【WCF系列】(四)WCF客户端怎么消费服务

    WCF客户端怎么消费服务 获取服务绑定协议.绑定和地址:实现方式 SvcUtil方式:SvcUtil.exe是一个命令行工具,位于:C:\Program Files (x86)\Microsoft S ...

  9. 动态的调用服务端的WCF中的方法

    客户端调用wcf ,有时需要动态的调用服务端的WCF中的方法,本方法,反射wcf 的接口,动态调用接口中的方法. 主要为,动态绑定,反射动态调用. public static object Execu ...

随机推荐

  1. windows系统调用 临界区机制

    #include "iostream" #include "windows.h" #include "cstring" using name ...

  2. 【01:转自知乎:关于 openSUSE 】

    我是 openSUSE 中文维基唯一的非官方维护者,openSUSE 简体中文翻译团队召集人,linuxsir SuSE 版块的版主,openSUSE 官方论坛 http://forums.opens ...

  3. SQL批量添加主键脚本

    --SQL批量添加主键脚本 --操作提示:运行此脚本前请记得备份您要操作的数据库 --实现功能:执行脚本后数据库中的所有数据表都会有一个主键 --添加规则1:主键名称为ID(可自己修改),数据类型为整 ...

  4. 在win 7 vs2013下 web 调试 出现“ iis Express Worker Process 已停止工作”错误

    在win 7 vs2013下 web 调试 出现“ iis Express Worker Process 已停止工作”错误: 如下图:   最终解决方案如下: 用管理员身份运行CMD,输入netsh  ...

  5. RecycleView可以策划的Item

    public abstract class SwipeMenuViewHolder { /* 从左侧滑出菜单 */ public static final int EDGE_LEFT = 1; /* ...

  6. javadoc 生成文档注释

    我们可以通过 javadoc 命令从文档注释中提取内容,生成程序的 API 帮助文档. javadoc -d doc demo.java 文档注释:/******/ @author 标明开发该类模块的 ...

  7. UBUNTU下Y86模拟器的安装和使用

    UBUNTU下Y86模拟器的安装和使用 由于上周在虚拟机中安装Y86模拟器中出现了一些问题并且没得到解决,所以上周实验是在实验楼上做的,这几天练习了UBUNTU下Y86模拟器的安装和使用. 参考博客: ...

  8. Centos6版本升级

    1.查看当前版本 [root@IDC-D-1699 docker]# cat /etc/issue CentOS release 6.8 (Final) Kernel \r on an \m 2.升级 ...

  9. (转) cocos2dx 显示中文

    cocos2dx 中文显示 分类: cocos2dx 2014-01-18 10:31 253人阅读 评论(0) 收藏 举报 目录(?)[+] 总结一句话,就是UTF-8编码. 1. 中文FNT字体文 ...

  10. Flowplayer-Skin

    SOURCE URL: https://flowplayer.org/docs/skinning.html Skinning with CSS3 Flowplayer skin design is C ...