WCF初探-10:WCF客戶端調用服務


創建WCF 服務客戶端應用程序需要執行下列步驟:

 

  • 獲取服務終結點的服務協定、綁定以及地址信息
  • 使用該信息創建 WCF 客戶端
  • 調用操作
  • 關閉該 WCF 客戶端對象

 

WCF客戶端調用服務存在以下特點:

 

  • 服務和客戶端使用托管屬性、接口和方法對協定進行建模。 若要連接客戶端應用程序中的服務,則需要獲取該服務協定的類型信息。通常,我們使用Svcutil.exe(ServiceModel Metadata Utility Tool)來完成,也可以直接在客戶端項目上引用服務地址完成。它們會從服務中下載元數據,並使用您選擇的語言將其轉換到托管源代碼文件中,同時還創建一個您可用於配置 WCF 客戶端對象的客戶端應用程序配置文件
  • WCF 客戶端是表示某一個WCF服務的一個本地對象,客戶端可以使用這種形式與遠程服務進行通信。 WCF 客戶端類型可以實現目標服務協定,因此當您創建一個服務協定,並對其進行配置后,就可以直接使用客戶端對象調用服務操作。 WCF 運行時將方法調用轉換為消息,然后將這些消息發送到服務,偵聽回復,並將這些值作為返回值或 out 參數(或 ref 參數)返回到 WCF 客戶端對象中。
  • 創建並配置了客戶端對象后,請創建一個 try/catch 塊,如果該對象是本地對象,則以相同的方式調用操作,然后關閉 WCF 客戶端對象。 當客戶端應用程序調用第一個操作時,WCF 將自動打開基礎通道,並在回收對象時關閉基礎通道。 (或者,還可以在調用其他操作之前或之后顯式打開和關閉該通道。)
  • 不應該使用 using 塊來調用WCF服務方法。因為C# 的“using”語句會導致調用 Dispose()。 它等效於 Close(),當發生網絡錯誤時可能會引發異常。 由於對 Dispose() 的調用是在“using”塊的右大括號處隱式發生的,因此導致異常的根源往往會被編寫代碼和閱讀代碼的人所忽略。 這是應用程序錯誤的潛在根源。

 

WCF客戶端調用服務方式:

 

  • WCF通信機制由它自身復雜的體系結構所決定,但WCF服務給我們提供了兩種不同的機制來創建客戶端程序調用,一種是ClientBase<TChannel>類,另一種是ChannelFactory<TChannel> 類。
  • ClientBase<TChannel>:創建客戶端代理類的基類,客戶端代理類通過繼承該基類,調用WCF的內部通信機制來實現WCF客戶端與服務端的通信。代理類是一個公開單個CLR接口來表示服務契約的CLR類,代理類和服務契約很相似,但是他有着附加的方法來管理代理的生命周期和連接服務。通過visual studio 右鍵添加服務引用和通過svcutil.exe命令行工具生成的客戶端都屬於這種方式。(如果不熟悉svcutil.exe,請參照WCF初探-1:認識WCF)
  • ChannelFactory<TChannel>:使用通道工廠類取決於你是否擁有描述服務契約的本地接口。最大的好處是你可以已擴展的方式更容易的修改通道的通信機制,如果你需要共享服務和客戶端之間的契約組件,那么使用ChannelFactory<TChannel>可以更有效的節省時間,但客戶端必須完成對服務契約組件的引用。
  • ClientBase<TChannel>和ChannelFactory<TChannel>的差異:

    

 

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; }
    }
}
View Code

    UserInfo.cs的代碼如下:  

  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Service
{
    public class UserInfo:IUserInfo
    {
        public User[] GetInfo(int? id=null)
        {
            List<User> Users = new List<User>();
            Users.Add(new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" });
            Users.Add(new User { ID = 2, Name = "TOM", Age = 18, Nationality = "JAPAN" });
            Users.Add(new User { ID = 3, Name = "SMITH", Age = 22, Nationality = "KOREA" });
            Users.Add(new User { ID = 4, Name = "ALENCE", Age = 21, Nationality = "INDIA" });
            Users.Add(new User { ID = 5, Name = "JOHN", Age = 22, Nationality = "SINGAPORE" });

            if (id != null)
            {
                return Users.Where(x => x.ID == id).ToArray();
            }
            else
            {
                return Users.ToArray();
            }
        }
    }
}
View Code    

    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();
            }
        }
    }
}
View Code

    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>
View Code   

    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();
            User[] Users = proxy.GetInfo(null);
            Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}","ID","Name","Age","Nationality");
            for(int i=0;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());
            }

            Console.Read();
        }
    }
}
View Code

    Client2:控制台應用程序,使用svcutil.exe工具生成客戶端代理類,在命令行中輸入以下命令:

    1.在運行中輸入cmd打開命令行,輸入 cd C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin

    2.輸入svcutil.exe /out:f:\ UserInfoClient.cs /config:f:\App.config http://localhost:1234/UserInfo/ (注意:端口號改成本機服務寄宿的端口號)    

    3.將生成的App.config和UserInfoClient.cs復制到Client2的工程目錄下,完成Program.cs代碼,代碼和Client1的代碼一樣。

    Client3:控制台應用程序,添加對Service程序集的引用,完成Program.cs代碼,代碼如下:  

  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel;
using System.ServiceModel.Channels;

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();


            User[] Users = channel.GetInfo(null);
            Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality");
            for (int i = 0; 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());
            }

            ((IChannel)channel).Close();
            factory.Close();
            Console.Read();
        }
    }
}
View Code

    查看服務運行結果,結果如下圖所示:

    

  • 客戶端代理類,我們以svcutil.exe生成的代理類文件為例子,查看UserInfoClient.cs,代碼如下:
namespace Service
{
    using System.Runtime.Serialization;
    
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="User", Namespace="http://schemas.datacontract.org/2004/07/Service")]
    public partial class User : object, System.Runtime.Serialization.IExtensibleDataObject
    {
        
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        private int AgeField;
        
        private int IDField;
        
        private string NameField;
        
        private string NationalityField;
        
        public System.Runtime.Serialization.ExtensionDataObject ExtensionData
        {
            get
            {
                return this.extensionDataField;
            }
            set
            {
                this.extensionDataField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int Age
        {
            get
            {
                return this.AgeField;
            }
            set
            {
                this.AgeField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int ID
        {
            get
            {
                return this.IDField;
            }
            set
            {
                this.IDField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string Name
        {
            get
            {
                return this.NameField;
            }
            set
            {
                this.NameField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string Nationality
        {
            get
            {
                return this.NationalityField;
            }
            set
            {
                this.NationalityField = value;
            }
        }
    }
}


[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.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IUserInfoChannel : IUserInfo, System.ServiceModel.IClientChannel
{
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class UserInfoClient : System.ServiceModel.ClientBase<IUserInfo>, IUserInfo
{
    
    public UserInfoClient()
    {
    }
    
    public UserInfoClient(string endpointConfigurationName) : 
            base(endpointConfigurationName)
    {
    }
    
    public UserInfoClient(string endpointConfigurationName, string remoteAddress) : 
            base(endpointConfigurationName, remoteAddress)
    {
    }
    
    public UserInfoClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(endpointConfigurationName, remoteAddress)
    {
    }
    
    public UserInfoClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress)
    {
    }
    
    public Service.User[] GetInfo(System.Nullable<int> id)
    {
        return base.Channel.GetInfo(id);
    }
}

    我們可以看到客戶端代理類中有着數據契約、服務契約,並且客戶端代理類UserInfoClient繼承ClientBase<IUserInfo>, IUserInfo,通過WCF內部通道機制完成對服務的調用。   

   public Service.User[] GetInfo(System.Nullable<int> id)

    {

          returnbase.Channel.GetInfo(id);

   }

    

  • ChannelFactory<IUserInfo>通道工廠通過:

    EndpointAddress address = new EndpointAddress("http://localhost:1234/UserInfo");

        WSHttpBinding binding = new WSHttpBinding();

        ChannelFactory<IUserInfo> factory = new ChannelFactory<IUserInfo>(binding,address);

        IUserInfochannel = factory.CreateChannel();     

        創建通道,完成對WCF服務的調用,注意:此處的工廠雖然提供了很多重載的方法,但我們這里屬於客戶端代碼編程,因此我們讀取不到配置文件信息,所以我把終結點信息

   和綁定信息寫到了代碼中,如果想通過ChannelFactory<IUserInfo>讀取配置文件信息,我們需要手動實現其擴展機制。這里以后的博文再做討論

  • 總結:我們通過實際的例子完成了對WCF客戶端調用服務的幾種方式的驗證,也完成了代理類和通道工廠對服務調用的相關概念的詮釋,在后面的博文中我將介紹客戶端調用WCF服務的其他特性。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM