zoukankan      html  css  js  c++  java
  • 【WCF--初入江湖】05 WCF异步编程

    05 WCF异步编程

      

    一、服务设计最佳实践

      在设计之初,是否用异步,应该由客户端来决定,而不应该去考虑服务的调用者调用的方式。

      

      优点:充分利用多核CPU,

         改善用户体验

      缺点:滥用异步,会影响性能

    二、实现异步的方式

      WCF有两种方式实现异步调用代理类:

    第一种:【方式1】用svcutil生成异步功能的代理类:

      

      方式是在svcutil后添加参数 /async

    svcutil /async http://localhost:5555/WCFService

         【方式二】

      右键服务引用--【配置服务引用】--在弹出框中,勾选【允许生成异步操作】

    第二种: 在客户端修改服务接口,添加异步方法:

       在服务端的服务接口是:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            byte[] GetFile(string fileName);
        }

      则在客户端的项目中重新定义一个接口IAsyncService1,使其继承服务端的接口IService1,并额外添加BeginXXX()和EndXXX()方法,具体如下所示:

    namespace Keasy5.WCF.Asyn.ByHand.Client
    {
        interface IAsyncService1 : IService1
        {
            [OperationContract(AsyncPattern = true)]
            IAsyncResult BeginGetFile(string fileName, AsyncCallback callback, object asyncState);
    
            byte[] EndGetFile(IAsyncResult asyncResult);
        }
    }

      

    三、实现方式

      

         方式一:在添加服务引用时或后,修改服务引用配置

       服务端的接口和实现类定义

      IServer.cs

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            string GetData(string message);
        }

      Server.cs   

        public class Service1 : IService1
        {
            public string GetData(string message)
            {
                System.Threading.Thread.Sleep(3000);
                return string.Format("You entered: {0}", message);
            }
        }

        具体的做法是:

      第一步:右键服务引用--【配置服务引用】--在弹出框中,勾选【允许生成异步操作】

          第二步:客户端调用异步方法,有两种调用方式:

          【方式一】客户端使用使用自动生成的代理类中的BeginXXX()和EndXXX()

            private void button2_Click(object sender, EventArgs e)
            {
                Service1Client service1Client = new Service1Client();
                service1Client.BeginGetData("自动生成的异步方法"
                    ,doCallBack,
                    service1Client);
    
                DoAfterCallWCFServer();
    
            }
    
            private void doCallBack(IAsyncResult asyncResult)
            {
                Service1Client client = (Service1Client) asyncResult.AsyncState;
                string message = client.EndGetData(asyncResult);
    
                MessageBox.Show(message);
    
            }
    
            private Void DoAfterCallWCFServer()
            {
                MessageBox.Show("调用WCF后的后续操作!");
            }

        【方式二:使用xxxCompleted事件/xxxAsync方法】

            private void button3_Click(object sender, EventArgs e)
            {
                Service1Client service1Client = new Service1Client();
                service1Client.GetDataCompleted +=service1Client_GetDataCompleted;
                service1Client.GetDataAsync("XXXCompleted/XXXAsync");              //开始异步调用
    
                DoAfterCallWCFServer();
            }
    
            private void service1Client_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
            {
                MessageBox.Show(e.Result);
            }
    
            private void DoAfterCallWCFServer()
            {
                MessageBox.Show("调用WCF后的后续操作!");
            }

      源代码下载:

      链接: http://pan.baidu.com/s/1gd1PNO3 密码: qtel

     方式二:在客户端修改服务接口,添加异步方法

       

      在客户端修改的服务接口,添加异步方法,

      在客户端决定采用异步方式调用的操作时,修改了客户端的服务契约接口,但并不影响服务端的契约定义,

        

      第一步:创建3个项目:

         【1】类库项目Keasy5.WCF.Asyn.Contract,用于定义客户端和服务端共同的契约

       【2】WinForm项目Keasy5.WCF.Asyn.ByHand.Client,作为客户端;其引用类库项目Keasy5.WCF.Asyn.Contract

       【3】WCF服务库项目Keasy5.WCF.Asyn.WCFServer,其引用类库项目Keasy5.WCF.Asyn.Contract

       第二步:类库项目Keasy5.WCF.Asyn.ByHand.Client添加接口IServer:

         IServer1.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.Text;
    
    namespace Keasy5.WCF.Asyn.Contract
    {
        [ServiceContract]
        public interface IService1
        {
            [OperationContract(Action = "", ReplyAction = "")]
            byte[] GetFile(string fileName);
        }
    }

      第二步:WCF服务库项目Keasy5.WCF.Asyn.WCFServer添加一个类Server1,用于实现类库项目Keasy5.WCF.Asyn.Contract添加接口IServer1:

         Server1.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.IO;
    
    using Keasy5.WCF.Asyn.Contract;
    
    namespace Keasy5.WCF.Asyn.WCFServer
    {
        public class Service1 : IService1
        {
            public byte[] GetFile(string fileName)
            {
                string path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, fileName);
                using (FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] bytes = new byte[fileStream.Length];
    
                    fileStream.Read(bytes, 0, (int)fileStream.Length);
                    return bytes; 
                }
            }
        }
    }
    View Code

      第三步:在客户端修改服务接口,添加异步方法

        在Keasy5.WCF.Asyn.ByHand.Client项目中添加一个接口IAsyncService1接口,并且其继承类库项目Keasy5.WCF.Asyn.Contract定义的接口IServer1

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using Keasy5.WCF.Asyn.Contract;
    
    namespace Keasy5.WCF.Asyn.ByHand.Client
    {
        [ServiceContract]
        interface IAsyncService1 : IService1
        {
            [OperationContract(AsyncPattern = true, Action = "", ReplyAction = "") ]
            IAsyncResult BeginGetFile(string fileName, AsyncCallback callback, object asyncState);
    
            byte[] EndGetFile(IAsyncResult asyncResult);
        }
    }

      

      第四步:客户端调用异步方法:

      【1】先在配置文件app.config中添加节点配置:

         

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <bindings>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_IService1" />
          </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Asyn.WCFServer/Service1/"
                    binding="basicHttpBinding" 
                    bindingConfiguration="BasicHttpBinding_IService1"
    
    contract
    ="Keasy5.WCF.Asyn.ByHand.Client.IAsyncService1"

    name="basicHttpIService1" /> </client> </system.serviceModel> </configuration>

    注意:节点的contract属性值是

    Keasy5.WCF.Asyn.ByHand.Client.IAsyncService1


    是客户端定义的接口IAsyncService1。

      【2】然后用管道工厂ChannelFactory<T>,创建服务管道调用服务:

            private void button1_Click(object sender, EventArgs e)
            {
                //使用通道工厂创建服务
                ChannelFactory<IAsyncService1> channelFactory = new ChannelFactory<IAsyncService1>("basicHttpIService1");
                IAsyncService1 asyncService1 = channelFactory.CreateChannel();
    
                asyncService1.BeginGetFile(@"abc.jpg", DoGetFileCallBack, asyncService1);
            }
    
            private void DoGetFileCallBack(IAsyncResult asyncResult)
            {
                IAsyncService1 asyncService1 = (IAsyncService1) asyncResult.AsyncState;
                byte[] recievedBytes = asyncService1.EndGetFile(asyncResult);
    
    
                if (recievedBytes.Length > 0)
                {
                    MemoryStream memoryStream = new MemoryStream(recievedBytes);
                    this.pictureBox1.Image = Image.FromStream(memoryStream); 
    
      
              MessageBox.Show("异步调用服务的后续操作");
    }
    else { MessageBox.Show("无法获取图片信息"); } }

       

     注意:常见的异常和处理方法:

    【1】当接口IAsyncService1没有添加[ServiceContract]特性,会出现如下异常:

    在 ServiceModel 客户端配置部分中,找不到名称“basicHttpIService1”和
    协定“Keasy5.WCF.Asyn.Contract.IService1”的终结点元素。
    这可能是因为未找到应用程序的配置文件,
    或者是因为客户端元素中找不到与此名称匹配的终结点元素。

    解决方案:

      在IAsyncService1没有添加[ServiceContract]特性

    【2】异常:

    类型“Keasy5.WCF.Asyn.Contract.IService1”中的同步 OperationContract 方法“GetFile”与异步 OperationContract 方法“BeginGetFile”和“EndGetFile”匹配,
    因为它们具有相同的操作名称“GetFile”。
    当同步 OperationContract 方法与一对异步 OperationContract 方法匹配时,
    这两个 OperationContracts 必须具有相同的“Action”属性值。
    在此情况下,值是不同的。要解决此问题,请更改其中一个 OperationContracts 的“Action”属性以与另一个匹配。此外,更改其中一个方法的名称将阻止匹配。

    解决方法:

    IServer1的GetFile方法和

    IAsyncSever1的BeginGetFile方法

    都添加如下特性:Action = "", ReplyAction = ""

    [OperationContract(Action = "", ReplyAction = "")]

    使它们的方法签名一样。

     【3】异常:

    在 ServiceModel 客户端配置部分中,找不到名称“basicHttpIService1”和
    协定“Keasy5.WCF.Asyn.ByHand.Client.IAsyncService1”的终结点元素。
    这可能是因为未找到应用程序的配置文件,或者是因为客户端元素中找不到与此名称匹配的终结点元素。

    这是由于客户端配置文件端点(endpoint )的Contract值是服务端的接口类型IServer1,而不是客户端的接口类型IAsyncServer1;

    错误的客户端配置:

        <client>
          <endpoint address="http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Asyn.WCFServer/Service1/"
                    binding="basicHttpBinding" 
                    bindingConfiguration="BasicHttpBinding_IService1"
                    contract="MyWCFService.IService1" 
                    name="basicHttpIService1" />
        </client>


    应该修改为客户端的接口类型IAsyncServer1:contract="Keasy5.WCF.Asyn.ByHand.Client.IAsyncService1",下面是正确的配置:

        <client>
          <endpoint address="http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Asyn.WCFServer/Service1/"
                    binding="basicHttpBinding" 
                    bindingConfiguration="BasicHttpBinding_IService1"
                    contract="Keasy5.WCF.Asyn.ByHand.Client.IAsyncService1" 
                    name="basicHttpIService1" />
        </client>

     源码下载:

    链接: http://pan.baidu.com/s/1c0nIOLi%20 密码: d71x

     

     

  • 相关阅读:
    Python动态展示遗传算法求解TSP旅行商问题
    MOEAD算法中均匀权向量的实现---Python
    HDU 5294 多校第一场1007题 最短路+最小割
    POJ 3261 Milk Patterns sa+二分
    HDU 4292 FOOD 2012 ACM/ICPC Asia Regional Chengdu Online
    CodeForces 201A Clear Symmetry
    POJ 1679 The Unique MST 确定MST是否唯一
    POJ 3268 Silver Cow Party 最短路 基础题
    POJ 2139 SIx Degrees of Cowvin Bacon 最短路 水題
    POJ2229 Sumsets 基礎DP
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/3825359.html
Copyright © 2011-2022 走看看