WCF初探-11:WCF客户端异步调用服务
前言:
- 在上一篇WCF初探-10:WCF客户端调用服务 中,我详细介绍了WCF客户端调用服务的方法,但是,这些操作都是同步进行的。有时我们需要长时间处理应用程序并得到返回结果,但又不想影响程序后面代码部分的执行,这时我们就需要考虑使用异步的方式来调用服务。注意这里的异步是完全针对客户端而言的,与WCF服务契约的方法是否异步无关,也就是在不改变操作契约的情况下,我们可以用同步或者异步的方式调用WCF服务。
WCF客户端异步调用服务方式:
- 通过代理类异步调用服务。就需要通过使用事件驱动的异步调用模型,客户端可以对此接口异步调用操作。基于事件的异步模型设计准则规定,如果返回了多个值,则一个值会作为 Result 属性返回,其他值会作为 EventArgs 对象上的属性返回。 因此产生的结果之一是,如果客户端使用基于事件的异步命令选项导入元数据,且该操作返回多个值,则默认的 EventArgs 对象返回一个值作为 Result 属性,返回的其余值是 EventArgs 对象的属性。如果要将消息对象作为 Result 属性来接收并要使返回的值作为该对象上的属性,请使用 /messageContract 命令选项。 这会生成一个签名,该签名会将响应消息作为 EventArgs 对象上的 Result 属性返回。 然后,所有内部返回值就都是响应消息对象的属性了。
- 使用通道工厂以异步方式调用操作。使用 ChannelFactory<TChannel> 时,不支持事件驱动的异步调用模型。这时我们需要异步重写服务契约的接口,每个方法都包含一组beginXXX和endXXX,XXX代表方法名。并且我们需要将异步操作的方法上的 AsyncPattern 属性设置为 true。前一个方法启动调用,而后一个方法在操作完成时检索结果。
WCF客户端异步调用服务示例:
- 工程结构如下图所示:
- 工程结构说明:
- 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事件里,通过参数类型GetInfoCompletedEventArgs的Result可以获得返回结果。而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客户端异步调用服务的更多相关文章
- WCF系列教程之客户端异步调用服务
本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一.简介 在前面的随笔中,详细的介绍了WCF客户端服务的调用方法 ...
- 通过.NET客户端异步调用Web API(C#)
在学习Web API的基础课程 Calling a Web API From a .NET Client (C#) 中,作者介绍了如何客户端调用WEB API,并给了示例代码. 但是,那些代码并不是非 ...
- Webservice客户端动态调用服务端功能方法
一.发布WebService服务 方式一:在服务端生成wsdl文件,下方客户端直接引用即可 优点:针对要发布的方法生成一个wsdl文件即可,无需多余配置. 缺点:每次服务端方法发生改变都需 ...
- cxf 生成客户端代码调用服务
cxf是另一种发布webservice的方式,与jdk提供的相比 jdk提供的是wsimport cxf 提供的是 wsdl2java- d 地址 根据http://www.cnblogs.com/f ...
- WCF初探文章列表
WCF初探-1:认识WCF WCF初探-6:WCF服务配置 WCF初探-2:手动实现WCF程序 WCF初探-7:WCF服务配置工具使用 WCF初探-3:WCF消息交换模式之单向模式 WCF初探-8:W ...
- WCF初探-15:WCF操作协定
前言: 在前面的文章中,我们定义服务协定时,在它的操作方法上都会加上OperationContract特性,此特性属于OperationContractAttribute 类,将OperationCo ...
- WCF初探-10:WCF客户端调用服务
创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: ...
- 【WCF系列】(四)WCF客户端怎么消费服务
WCF客户端怎么消费服务 获取服务绑定协议.绑定和地址:实现方式 SvcUtil方式:SvcUtil.exe是一个命令行工具,位于:C:\Program Files (x86)\Microsoft S ...
- 动态的调用服务端的WCF中的方法
客户端调用wcf ,有时需要动态的调用服务端的WCF中的方法,本方法,反射wcf 的接口,动态调用接口中的方法. 主要为,动态绑定,反射动态调用. public static object Execu ...
随机推荐
- winform 窗体移动API、窗体阴影API
//窗体移动API [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport ...
- 体验极速Android SDK的更新与下载
首先:国内明确一点,国内由于天朝限制了google,更新和下载Android相关资料都比较吃力,因此,本文正式宣告,此问题不再是问题-------别说话,吻我 先给点福利: 关于java(Androi ...
- POJ - 1245 Programmer, Rank Thyself
POJ - 1245 Programmer, Rank Thyself Time Limit: 1000MS Memory Limit: 10000KB 64bit IO Format: %I64d ...
- C#计算器代码
在刚刚接触c#的时候,就想做一个简单加减乘除计算器.这就是目标,可惜一直没有动手去做,今天特意把它简单做了.很简单,很简单,了却一个心愿了. 代码: using System; using Syste ...
- 精彩的javascript对象和数组混合相加
最近遇到一个让人困解的一个问题:一个简单的js加法运算表达式: +[]; //这里加上一个空数组得到什么???答案:'1'; 为什么答案是1,一开始我也很困惑:后来我读了一篇文章才知道:在javasc ...
- POJ 2420:A Star not a Tree?
原文链接:https://www.dreamwings.cn/poj2420/2838.html A Star not a Tree? Time Limit: 1000MS Memory Limi ...
- hibernate的二级缓存
缓存(Cache): 计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能.缓存中的数 ...
- 2016年12月18日 星期日 --出埃及记 Exodus 21:13
2016年12月18日 星期日 --出埃及记 Exodus 21:13 However, if he does not do it intentionally, but God lets it hap ...
- MySql数据库-使用cmd操作数据库
寄语: 针对一些公司对测试岗位掌握SQL的要求,本博文以此献给没有掌握数据库语句知识的功能测试人员,愿与广大测试同胞共同进步. 如果电脑上已安装配置好MySQL数据库,打开命令提示符,按照下图以此操作 ...
- Python3基础 把一个列表中内容给另外一个列表,形成两个独立的列表
镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...