這篇文章是幾年前本身學習WCF時的讀書筆記,整理文檔的時候發現了順便放到博客裏面。app
示例是網上流傳很廣的一個訂票系統的示例,因爲在學習過程當中出現了一些錯誤,在此特意把爲何出錯的緣由記錄下來。ide
新建一個WCF服務應用程序學習
在IService1.cs定義服務契約測試
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace WcfDemo { // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼和配置文件中的接口名「IService1」。 [ServiceContract] public interface IService1 { [OperationContract] void AddTicket(int count); [OperationContract] int BuyTickets(int num); [OperationContract] int GetRemainingNum(); // TODO: 在此添加您的服務操做 } // 使用下面示例中說明的數據約定將複合類型添加到服務操做。 [DataContract] public class Ticket { bool boolCount = true; int howMany = 10; [DataMember] /* 判斷是否還有票 */ public bool BoolCalue { get { return boolCount; } set { if (howMany > 0) { boolCount = false; } else { boolCount = true; } } } [DataMember] public int HowMany { get { return howMany; } set { howMany = value; } } } }
在Service1.svc.cs中實現契約服務ui
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace WcfDemo { // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼、svc 和配置文件中的類名「Service1」。 //[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class Service1 : IService1 { Ticket T = new Ticket(); public void AddTicket(int count) { T.HowMany = T.HowMany + count; } public int GetRemainingNum() { return T.HowMany; } public int BuyTickets(int Num) { if (T.BoolCalue) { T.HowMany = T.HowMany - Num; return 1; } else { return 0; } } } }
如今添加宿主程序用於監測服務,添加WinForm項目加入解決方案this
WinForm宿主程序界面以下圖:spa
添加WCF服務應用程序生成的dll文件debug
配置以下圖,在解決方案的屬性裏面須要把啓動項目設置成宿主程序,不然每次F5啓動都會失敗代理
源代碼調試
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.ServiceModel; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } ServiceHost host = null; private void button1_Click(object sender, EventArgs e) { host = new ServiceHost(typeof(WcfDemo.Service1)); host.Open(); this.label1.Text = "服務已啓動"; } private void button2_Click(object sender, EventArgs e) { if (host.State != CommunicationState.Closed) { host.Close(); } this.label1.Text = "服務已關閉"; } } }
接下來配置宿主程序的app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services><!--添加服務--> <service name="WcfDemo.Service1" behaviorConfiguration="CalculatorServiceBehavior"> <!--name 必須與代碼中的host實例初始化的服務同樣 behaviorConfiguration 行爲配置 --> <host> <baseAddresses> <!--添加調用服務地址--> <add baseAddress="http://localhost:8000/"/> </baseAddresses> </host> <!--添加契約接口 contract="WcfDemo.IService1" WcfDemo.IService1爲契約接口 binding="wsHttpBinding" wsHttpBinding爲經過Http調用.若是下面的節點不存在,那麼默認的綁定方式是basicHttpBinding。--> <endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint> </service> </services> <!--定義CalculatorServiceBehavior的行爲--> <behaviors> <serviceBehaviors> <behavior name="CalculatorServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
程序運行結果:
服務啓動後能夠經過app.config中baseAddress節點中的baseAddress地址查看WCF服務
到此爲止服務以及宿主程序都已經建立好了,下面該建立測試客戶機了。
新建個WinForm程序做爲咱們的測試客戶機
界面以下:
購買車票:調用WCF服務的BuyTickets()方法
查詢車票:調用WCF服務的GetRemainingNum()方法
Label用於顯示調用結果
爲項目添加服務引用,地址輸入宿主程序app.config中baseAddress地址
後臺代碼爲
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WCFClient { public partial class Form1 : Form { ServiceReference2.Service1Client TClient = new ServiceReference2.Service1Client(); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int i = TClient.BuyTickets(2); if (i == 1) { this.label1.Text = "購買成功"; } this.label1.Text += "剩餘車票還有:" + TClient.GetRemainingNum().ToString(); } private void button2_Click(object sender, EventArgs e) { this.label1.Text = ""; this.label1.Text = TClient.GetRemainingNum().ToString(); } } }
點擊購買車票時的結果以下,和網上的示例獲得的結果不同。
使用debug調試發現,每次調用一個方法,Ticket都要從新生成,查詢資料發現,這和服務實例的生命週期有關。在宿主程序的app.config配置裏面,我少寫了
<endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint>
致使默認的綁定方式是basicHttpBinding,而且服務實例的生命週期是Per-Call的,也即每次調用服務方法,服務實例都要從新生成。在宿主程序裏面把app.config修改,加上
<endpoint address="" binding="wsHttpBinding" contract="WcfDemo.IService1"></endpoint>
從新生成服務端,而後運行客戶端,報錯以下
這是因爲宿主程序的binding方式改變了,可是客戶端的代理類的binding方式沒有作相應的修改致使的,從新把代理類生成一次,如前面的圖所示我從新生成了代理類ServiceReference2。
再次運行客戶端程序而後打擊購買車票,獲得了預期的結果還剩8張車票。使用wsHttpBinding的時候,默認的服務實例生命週期是Per-Session,這樣在一個會話裏面調用服務方法,服務實例不會每次都從新生成。
客戶端app.config以下
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <security mode="None"> <transport clientCredentialType="None" proxyCredentialType="None" realm="" /> <message clientCredentialType="UserName" algorithmSuite="Default" /> </security> </binding> </basicHttpBinding> <wsHttpBinding> <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" /> <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" /> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="http://localhost:8000/" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" /> <endpoint address="http://localhost:8000/" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference2.IService1" name="WSHttpBinding_IService1"> <identity> <userPrincipalName value="Administrator@wmq.kxu.com" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>