• 【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

     

     

  • 相关阅读:
    ubuntu mysql 的安装与简单操作
    JavaScript中的返回函数的函数
    JavaScript中的作用域
    JavaScript中的面向对象【一】
    JavaScript中的函数是数据
    JavaScript中的面向对象【二】
    JavaScript中的函数重写自己
    JavaScript中的匿名函数、回调函数、自调用函数
    JavaScript中的闭包
    mysql添加索引
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/3825359.html
Copyright © 2020-2023  润新知