老老實實學WCF
第二篇 配置WCF
?
在上一篇中,我們在一個控制臺應用程序中編寫了一個簡單的WCF服務并承載了它。先回顧一下服務端的代碼:
?
[csharp] view plaincopyprint?
using?System;?? using?System.Collections.Generic;?? using?System.Linq;?? using?System.Text;?? ?? using?System.ServiceModel;?? using?System.ServiceModel.Description;?? ?? namespace?HelloWCFService?? {?? ????class?Program?? ????{?? ????????static?void?Main(string[]?args)?? ????????{?? ????????????Uri?baseAddress?=?new?Uri("http://localhost:8000/MyService");?? ?? ????????????ServiceHost?host?=?new?ServiceHost(typeof(HelloWCFService),?baseAddress);?? ?? ????????????host.AddServiceEndpoint(typeof(IHelloWCFService),?new?WSHttpBinding(),?"HelloWCFService");?? ?? ????????????ServiceMetadataBehavior?smb?=?new?ServiceMetadataBehavior();?? ????????????smb.HttpGetEnabled?=?true;?? ????????????host.Description.Behaviors.Add(smb);?? ?? ????????????host.Open();?? ?? ????????????Console.WriteLine("Service?is?Ready");?? ????????????Console.WriteLine("Press?Any?Key?to?Terminate...");?? ????????????Console.ReadLine();?? ?? ????????????host.Close();?? ?????????????? ????????}?? ????}?? ?? ????[ServiceContract]?? ????interface?IHelloWCFService?? ????{?? ????????[OperationContract]?? ????????string?HelloWCF();?? ????}?? ?? ????public?class?HelloWCFService?:?IHelloWCFService?? ????{?? ????????public?string?HelloWCF()?? ????????{?? ????????????return?"Hello?WCF!";?? ????????}?? ????}?? }?? using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;using System.ServiceModel;
using System.ServiceModel.Description;namespace HelloWCFService
{class Program{static void Main(string[] args){Uri baseAddress = new Uri("http://localhost:8000/MyService");ServiceHost host = new ServiceHost(typeof(HelloWCFService), baseAddress);host.AddServiceEndpoint(typeof(IHelloWCFService), new WSHttpBinding(), "HelloWCFService");ServiceMetadataBehavior smb = new ServiceMetadataBehavior();smb.HttpGetEnabled = true;host.Description.Behaviors.Add(smb);host.Open();Console.WriteLine("Service is Ready");Console.WriteLine("Press Any Key to Terminate...");Console.ReadLine();host.Close();}}[ServiceContract]interface IHelloWCFService{[OperationContract]string HelloWCF();}public class HelloWCFService : IHelloWCFService{public string HelloWCF(){return "Hello WCF!";}}
}
所有的這些代碼都寫在program.cs中,干凈清爽。
?
我們稍微審視一下這段程序會發現,我們用了很多的代碼來定制服務的特性,例如基地址、終結點、綁定、行為等。這些都叫做配置。而真正對服務的本身的定義是很少的(主邏輯就是返回一個字符串而已),因此我們不難看出,WCF的編程中配置占了很大的比重。
?
WCF的配置選項是很多的,我們這里只考慮最簡單的情況。我們在定義和實現了服務協定后,至少應該做哪些配置才能讓服務運行起來呢?
(1) 依據服務實現類配置一個服務(ServiceHost)。
(2) 指定一個基地址(如果終結點中指定了絕對地址,這步可以省略)。
(3) 建立一個終結點,并為其指定地址、綁定和服務協定。
(4) 建立一個元數據交換終結點。
(5) 為服務添加一個行為來啟用元數據交換。
雖然在.Net 4.0下微軟提供了簡化配置,我們甚至可以一行配置都不做,但是為了搞清楚配置的基本原理,我們暫時不考慮簡化配置的情況。
?
以下這些配置是我們必須要做的,我們從代碼中可以看到以上幾種配置相應語句:
?
建立基地址:
[csharp] view plaincopyprint?
Uri?baseAddress?=?new?Uri("http://localhost:8000/MyService");?? Uri baseAddress = new Uri("http://localhost:8000/MyService");
建立服務:
[csharp] view plaincopyprint?
ServiceHost?host?=?new?ServiceHost(typeof(HelloWCFService),?baseAddress);?? ServiceHost host = new ServiceHost(typeof(HelloWCFService), baseAddress);
建立終結點并指定地址、綁定和服務協定:
[csharp] view plaincopyprint?
host.AddServiceEndpoint(typeof(IHelloWCFService),?new?WSHttpBinding(),?"HelloWCFService");?? host.AddServiceEndpoint(typeof(IHelloWCFService), new WSHttpBinding(), "HelloWCFService");
添加元數據交換終結點并添加啟用元數據交換行為
[csharp] view plaincopyprint?
ServiceMetadataBehavior?smb?=?new?ServiceMetadataBehavior();?? smb.HttpGetEnabled?=?true;?? host.Description.Behaviors.Add(smb);?? ServiceMetadataBehavior smb = new ServiceMetadataBehavior();smb.HttpGetEnabled = true;host.Description.Behaviors.Add(smb);
看上去清楚明白,但是只是看上去很美,這樣的配置方式存在弊端,例如基地址,如果當服務部署之后遷移了服務器,基地址發生變化,我們必須修改源程序并重新編譯重新部署才能實現這個要求。對于其他的配置選項亦是如此。這對于產品環境是不能接受的。好在WCF提供針對這個問題的解決方案:配置文件。
?
我們把對服務的配置寫在應用程序的配置文件中(IIS程序是web.config 其他程序是app.config),當配置發生改變的時候我們就不用重新編譯程序集了。
?
配置文件的寫法很復雜,有很多選項,為了便于上手,我們先從跟本例相關的選項開始。
?
在配置文件中,根節是<configuration>,所有的配置元素都位于其中。對于WCF服務的配置部分,最外層的節是<system.serviceModel>,所以配置文件中至少先應該是這個樣子:
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel></system.serviceModel>
</configuration>
現在我們準備開始配置一個服務,服務配置元素標簽為<services></services>,是<system.serviceModel>的子節,在一個宿主上可以承載許多服務,每一個服務用<service></service>來配置,它是<services>的子節。在配置<service>前,我們還要先添加一個基地址配置,基地址用<baseaddress>描述,他是<host>的子節,<host>是<service>的子節。
暈了么...慢慢來。
先把<services>節加上,這里可以容納許多服務,注意這個是帶s的
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ???????? ????</services>?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services></services> </system.serviceModel>
</configuration>
在<services>的懷抱中,我們添加一個<service>,這是我們要配置的服務本體,注意這個是不帶s的
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service>?? ?????????? ??????</service>?? ????</services>?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service></service></services> </system.serviceModel>
</configuration>
在<service>中,添加一個基地址,先添加一個<host>再添加一個<baseaddress>
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service>?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ??????</service>?? ????</services>?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host></service></services> </system.serviceModel>
</configuration>
?
到這里,基地址的部分已經完成,對應代碼中的位置你找到了么?
服務的配置還差一點,我們在代碼中為服務指定了實現類型,在配置文件中如何指定呢?就用<service>標簽的name屬性,指定的時候后要用完全限定名(帶著命名空間)
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service?name="HelloWCFService.HelloWCFService">?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ??????</service>?? ????</services>?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service name="HelloWCFService.HelloWCFService"><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host></service></services> </system.serviceModel>
</configuration>
我這個例子舉的不好了,命名空間和實現類型是一個名字,要注意區分。
?
到這里,服務也配置完了,對應代碼的位置翻上去找一下。
接下來配置終結點,終結點用<endpoint>元素表示,正如代碼實現中的參數,在配置中也需要一一指定地址、綁定和服務協定接口,分別用address、binding和contract屬性來表示,當然<endpoint>也是<service>的子節,畢竟他是屬于服務的嘛。
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service?name="HelloWCFService.HelloWCFService">?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ????????<endpoint?address="HelloWCFService"?binding="wsHttpBinding"?contract="HelloWCFService.IHelloWCFService"?/>?? ??????</service>?? ????</services>?????? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service name="HelloWCFService.HelloWCFService"><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host><endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" /></service></services> </system.serviceModel>
</configuration>
這里用了相對地址"HelloWCFService",他會和基地址組合在一起(排在后面)成為終結點的地址,這里也可以指定為空字符串"",此時基地址(即服務地址)就是終結點的地址,還可以指定一個絕對地址,那樣他會覆蓋基地址,基地址對這個終結點來說就不起作用了,這里可以看出,終結點的地址是獨立的,可以是基地址的子地址,也可以獨立使用另一個地址,他們之間沒有必然的鏈接。
這里的contract 同<service>里面一樣,也要使用完全限定名稱(帶上命名空間)。
注意,在使用IIS承載的時候,必須使用相對地址,也就是終結點必須是基地址的子地址,這是由IIS的部署結構決定的。
?
到這里終結點也配置完成,對應代碼的位置找到了嗎?
?
接下來是最后一步(或者說兩步),配置元數據交換終結點并開啟元數據交換行為。這個過程,代碼中用了三行,實際上代碼這三行僅僅是添加了元數據交換行為,并沒有配置元數據交換終結點,運行時系統為我們自動添加了終結點。這兩點缺一不可,雖然系統會為我們添加,我們還是要知道這個配置的寫法。這個很重要。
?
開啟元數據交換從原理上應該是兩件事,第一是服務允許元數據交換,第二是服務提供元數據交換方式,第一條就是添加元數據交換行為,表示服務允許這樣的請求,第二條就是服務告訴你如何請求,客戶端是通過一個固定的終結點去請求的,這個終結點的地址、綁定和協定都是固定的,我們不能更改,這個是框架的約定,我們只能按要求配置。
?
首先,第一條,允許元數據交換,這個是通過為服務添加一個行為來實現的,行為是用來描述服務的特性的,不同的服務可能有相同的行為,行為并不是歸某服務獨有的,因此行為被定義為<system.serviceModel>節的子節,可以被不同的服務引用,他的定義有些像服務,外面是帶s的,里面是不帶s的,畢竟可能有許多的行為定義嘛。
先定義個行為:
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service?name="HelloWCFService.HelloWCFService">?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ????????<endpoint?address="HelloWCFService"?binding="wsHttpBinding"?contract="HelloWCFService.IHelloWCFService"?/>?? ??????</service>?? ????</services>?? ????<behaviors>?? ??????<serviceBehaviors>?? ????????<behavior?name="metaExchange">?? ??????????<serviceMetadata?httpGetEnabled="true"/>?? ????????</behavior>?? ??????</serviceBehaviors>?? ????</behaviors>?? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service name="HelloWCFService.HelloWCFService"><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host><endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" /></service></services><behaviors><serviceBehaviors><behavior name="metaExchange"><serviceMetadata httpGetEnabled="true"/></behavior></serviceBehaviors></behaviors></system.serviceModel>
</configuration>
因為存在服務行為和終結點行為之分,所有<behaviors>和<behavior>之間又套了一個<serviceBehaviors>,表示這個是服務行為,我們為行為制定了名字,好讓<service>可以引用,也可以不指定,那么所有服務都會應用。交換元數據的行為有固定的標簽描述,就是<serviceMetadata>,對著代碼看,很熟悉吧。
?
建立了行為以后,要讓我們剛才建立的服務去引用他,用<service>的behaviorConfiguration屬性:
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service?name="HelloWCFService.HelloWCFService"behaviorConfiguration="metaExchange">?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ????????<endpoint?address="HelloWCFService"?binding="wsHttpBinding"?contract="HelloWCFService.IHelloWCFService"?/>?? ??????</service>?? ????</services>?? ????<behaviors>?? ??????<serviceBehaviors>?? ????????<behavior?name="metaExchange">?? ??????????<serviceMetadata?httpGetEnabled="true"/>?? ????????</behavior>?? ??????</serviceBehaviors>?? ????</behaviors>?? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service name="HelloWCFService.HelloWCFService"behaviorConfiguration="metaExchange"><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host><endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" /></service></services><behaviors><serviceBehaviors><behavior name="metaExchange"><serviceMetadata httpGetEnabled="true"/></behavior></serviceBehaviors></behaviors></system.serviceModel>
</configuration>
?
接下來第二步,建立元數據交換終結點,建立的位置和我們剛才建立的終結點位置相同,但是屬性是固定的,大小寫都不能寫錯。
[html] view plaincopyprint?
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<system.serviceModel>?? ????<services>?? ??????<service?name="HelloWCFService.HelloWCFService"?behaviorConfiguration="metaExchange">?? ????????<host>?? ??????????<baseAddresses>?? ????????????<add?baseAddress="http://localhost:8000/MyService"/>?? ??????????</baseAddresses>?? ????????</host>?? ????????<endpoint?address="HelloWCFService"?binding="wsHttpBinding"?contract="HelloWCFService.IHelloWCFService"?/>?? ????????<endpoint?address="mex"?binding="mexHttpBinding"?contract="IMetadataExchange"/>?? ??????</service>?? ????</services>?? ????<behaviors>?? ??????<serviceBehaviors>?? ????????<behavior?name="metaExchange">?? ??????????<serviceMetadata?httpGetEnabled="true"/>?? ????????</behavior>?? ??????</serviceBehaviors>?? ????</behaviors>?? ??</system.serviceModel>?? </configuration>?? <?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><services><service name="HelloWCFService.HelloWCFService" behaviorConfiguration="metaExchange"><host><baseAddresses><add baseAddress="http://localhost:8000/MyService"/></baseAddresses></host><endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" /><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></service></services><behaviors><serviceBehaviors><behavior name="metaExchange"><serviceMetadata httpGetEnabled="true"/></behavior></serviceBehaviors></behaviors></system.serviceModel>
</configuration>
到這里,配置文件就寫完了。我們把它放到程序里面去,打開上一篇中建立的服務端程序,為程序添加一個配置文件
右鍵點擊項目->添加->新建項->應用程序配置文件,名字系統會起好(app.config)。把上面的配置寫進去,保存。
既然我們已經有了配置文件,就不需要(也不應該)在代碼中配置了。代碼中的配置會覆蓋掉配置文件中的配置。所以我們要對代碼修改一下。
main函數中只留下這么幾行:
[csharp] view plaincopyprint?
ServiceHost?host?=?new?ServiceHost(typeof(HelloWCFService));?? ?? host.Open();?? ?? Console.WriteLine("Service?is?Ready");?? Console.WriteLine("Press?Any?Key?to?Terminate...");?? Console.ReadLine();?? ?? host.Close();?? ServiceHost host = new ServiceHost(typeof(HelloWCFService));host.Open();Console.WriteLine("Service is Ready");Console.WriteLine("Press Any Key to Terminate...");Console.ReadLine();host.Close();
其中,建立SeviceHost 那行被修改了,去掉了baseAddress的參數,但是我們仍需要告訴host 我們要寄存的服務類的類型。
F5運行起來。
然后在瀏覽器中訪問一下服務試試
[html] view plaincopyprint?
http://localhost:8000/MyService?? http://localhost:8000/MyService
是不是和昨天的結果一樣呢。
(CSDN的傳圖好象掛了哩...)
?
總結一下今天的學習。
我們使用配置文件的方法完成了對WCF服務的配置,從中接觸到了服務、終結點和行為的配置方法。配置文件的元素還有許多,像綁定、安全性等等特性。在今后學到的時候再慢慢展開,配置文件的每一個元素都應該力求背著寫下來,一行一行的寫,在寫的過程中體會,而不是四處復制和粘貼,這樣才能對配置文件的寫法有深刻的印象。
?
相關資源
徐長龍老師播講的《跟我一起從零開始學WCF系列課程》
http://msdn.microsoft.com/zh-cn/hh148206
?
MSDN技術資源庫中的WCF參考
http://msdn.microsoft.com/zh-cn/library/dd456779.aspx
總結
以上是生活随笔 為你收集整理的[老老实实学WCF] 第二篇 配置WCF 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。