在前面的文章中講述過WCF服務的宿主程序主要包括:三種,在那篇文章中,簡單的描述瞭如何把一個WCF服務寄宿到IIS上面,這篇文章中將具體講述如何把一個WCF服務寄宿到IIS上面。web
1、新建一個WCF服務應用程序:app
文件->新建->項目:選擇WCF下面的WCF服務應用程序ide
2、分析WcfSvcDemo項目,該項目的結構以下:測試
在該項目中,會默認生成一個IService1.cs的文件和Service1.svc文件。Service1.svc文件封裝的就是提供給客戶端的服務引用。
首先查看IService1.cs文件,從名字上面就能夠看出這是一個接口文件,裏面定義了一個接口IService1,接口上面使用了ServiceContract,意思是把這個接口聲明爲服務契約,服務契約是對客戶端而言的,就是這個接口能夠暴露出來讓客戶端能夠看見。接口裏面定義了兩個方法,每一個方法上面都使用了[OperationContract],意思是把這兩個方法聲明爲操做契約。只有把接口裏面的方法聲明爲操做契約,在客戶端裏面才能夠看到相應的方法,不然在客戶端裏面看不到在接口裏面定義的方法。網站
在來看Service.svc文件,能夠看到下面有一個Service.svc.cs文件,這個文件裏面定義了一個繼承IService1接口的類Service1,並實現了IService1接口裏面的方法。spa
刪除Service.svc.cs文件,能夠查看Service.svc文件,該文件裏面就一行代碼;debug
1 <%@ ServiceHost Language="C#" Debug="true" Service="WcfSvcDemo.Service1" CodeBehind="Service1.svc.cs" %>
這裏面有兩個重要的參數:Service和CodeBehind。Service是屬性值是WCF的服務實現類的徹底限定名。CodeBehind是服務實現類所在的文件名。在運行的時候,宿主程序從svc文件中的Service屬性獲得WCF服務的徹底限定名,而後從配置文件中找到同名的servicce,進而找到全部的EndPoint,並根據其屬性進行實例化。
配置文件中的Service名字必須是Service類名的徹底限定名(即Namespace.classname),EndPoint的Contract必須是Service接口的徹底限定名。不然,程序就沒法從程序集中找到相應的類進行加載。
注意:若是要修改接口實現類的名稱,必須使用「重構」的方式進行修改,由於只有利用「重構」的方式修改Servie類名的時候,.svc文件裏面Service的屬性值纔會被修改,利用其它方式修改類名,.svc文件裏面Service的屬性值會保留原值,這樣在運行的時候,根據svc裏面Service的屬性值查找不到相應的類,程序就會報錯。3d
svc文件裏面還有一個重要的參數:ServiceHostFactory。ServiceHostFactory旨在解決從IIS或WAS中訪問自定義ServiceHost的問題。由於從ServiceHost派生的自定義宿主是動態配置的而且可能爲各類類型,因此宿主環境從不會直接將其實例化。相反,WCF使用工廠模式提供宿主環境和服務的具體類型之間的間接層。除非進行通知,不然它使用返回ServiceHost的實例的ServiceHostFactory的默認實現。(在新建的svc文件中默認實現就是CodeBehind屬性的值)。但也能夠經過在@ServiceHost指令中指定工廠實現的CLR類型名稱來提供本身的工廠(用於返回派生宿主)。代理
下面是用於返回派生的ServiceHost的自定義ServiceHostFactory:調試
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.ServiceModel.Activation; 7 8 namespace Public.CustomService 9 { 10 public class CustomServiceHostFactory : ServiceHostFactory 11 { 12 protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 13 { 14 CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses); 15 return customServiceHost; 16 } 17 } 18 }
其中CustomServiceHost是自定義的繼承自ServiceHost的類,用於讀取配置文件的配置,CustomServiceHost類的定義以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.ServiceModel.Activation; 7 8 namespace Public.CustomService 9 { 10 public class CustomServiceHost :ServiceHost 11 { 12 public CustomServiceHost(Type serviceType, params Uri[] baseAddresses) 13 : base(serviceType, baseAddresses) 14 { 15 //加載Web.config的配置 16 log4net.Config.XmlConfigurator.Configure(); 17 } 18 protected override void ApplyConfiguration() 19 { 20 base.ApplyConfiguration(); 21 } 22 } 23 }
若要使用此工廠,而不使用默認工廠,則應該在@ServiceHost指令中提供相應的類型名稱:
1 <%@ ServiceHost Service="CustomSvcDemo.DatabaseService" Factory="Public.CustomService.CustomServiceHostFactory" %>
其中Service是實現類的徹底限定名,Factory是自定義ServiceHostFactory的徹底限定名,Public是一個dll文件。
若要使用此工廠,而不使用默認工廠,則應該在@ServiceHost指令中提供相應的類型名稱:
儘管對於從CreateServiceHost返回的ServiceHost能夠執行什麼操做沒有技術限制,但建議您儘量使工廠實現簡單化。若是有大量的自定義邏輯,最好將這些邏輯放入宿主內而不是工廠內,以即可以重用它們。
應在這裏說起另外一個承載API的層。WCF還具備ServiceHostBase和ServiceHostFactoryBase,可從中分別派生ServiceHost和ServiceHostFactory。對於您必須經過本身的自定義建立來交換元數據系統的大型組件的更高級方案,存在上述這些特性。
下面經過兩個具體的示例程序分別實現上面描述的默認工廠和自定義工廠。
3、使用默認工廠方式
刪除新建項目時自動建立的IService1.cs和Service1.svc文件,而後添加一個svc文件,在項目上面右鍵->添加->新建項:
在新建項裏面選擇web裏面的WCF服務,命名爲MyService:
點「添加」,除了建立MyService.svc文件之外,還會自動建立一個名爲IMyService.cs的接口文件,MyService.svc.cs裏面的MyService默認實現IMyService接口.
刪除IMyService接口裏面自動生成的方法,添加一個GetCurrentTime的方法,用來返回當前的時間,IMyService接口定義以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.ServiceModel; 6 using System.Text; 7 8 namespace WcfSvcDemo 9 { 10 // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼和配置文件中的接口名「IMyService」。 11 [ServiceContract] 12 public interface IMyService 13 { 14 /// <summary> 15 /// 獲取當前時間 16 /// </summary> 17 /// <returns></returns> 18 [OperationContract] 19 DateTime GetCurrentTime(); 20 } 21 }
四、MyService.svc.cs裏面的MyService類實現IMyService接口,MyService類定義以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.ServiceModel; 6 using System.Text; 7 8 namespace WcfSvcDemo 9 { 10 // 注意: 使用「重構」菜單上的「重命名」命令,能夠同時更改代碼、svc 和配置文件中的類名「MyService」。 11 // 注意: 爲了啓動 WCF 測試客戶端以測試此服務,請在解決方案資源管理器中選擇 MyService.svc 或 MyService.svc.cs,而後開始調試。 12 public class MyService : IMyService 13 { 14 /// <summary> 15 /// 返回當前時間 16 /// </summary> 17 /// <returns></returns> 18 public DateTime GetCurrentTime() 19 { 20 return DateTime.Now; 21 } 22 } 23 }
五、修改配置文件,增長service、binding等節點,修改後的配置文件以下:
1 <?xml version="1.0"?> 2 <configuration> 3 <appSettings/> 4 <system.web> 5 <compilation debug="true" targetFramework="4.0"/> 6 <httpRuntime/> 7 </system.web> 8 <system.serviceModel> 9 <services> 10 <service behaviorConfiguration="metadataBehavior" name="WcfSvcDemo.MyService"> 11 <endpoint address="" binding="basicHttpBinding" bindingConfiguration="Contract" name="MyService" contract="WcfSvcDemo.IMyService"/> 12 </service> 13 </services> 14 <behaviors> 15 <serviceBehaviors> 16 <behavior name="metadataBehavior"> 17 <!-- 爲避免泄漏元數據信息,請在部署前將如下值設置爲 false --> 18 <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> 19 <!-- 要接收故障異常詳細信息以進行調試,請將如下值設置爲 true。在部署前設置爲 false 以免泄漏異常信息 --> 20 <serviceDebug includeExceptionDetailInFaults="false"/> 21 </behavior> 22 </serviceBehaviors> 23 </behaviors> 24 <bindings> 25 <basicHttpBinding> 26 <binding name="Contract" closeTimeout="00:00:05" openTimeout="00:00:05" receiveTimeout="11:00:00" sendTimeout="11:00:00" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transferMode="Buffered"> 27 <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/> 28 </binding> 29 </basicHttpBinding> 30 </bindings> 31 <protocolMapping> 32 <add binding="basicHttpsBinding" scheme="https"/> 33 </protocolMapping> 34 <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/> 35 </system.serviceModel> 36 <system.webServer> 37 <modules runAllManagedModulesForAllRequests="true"/> 38 <!-- 39 若要在調試過程當中瀏覽 Web 應用程序根目錄,請將下面的值設置爲 True。 40 在部署以前將該值設置爲 False 可避免泄露 Web 應用程序文件夾信息。 41 --> 42 <directoryBrowse enabled="true"/> 43 </system.webServer> 44 </configuration>
主要是修改service節點裏面的name是服務實現類的徹底限定名,contract是服務接口的徹底限定名。
六、把WCF服務部署到IIS上面
在IIS上面網站->添加網站:
配置網站名稱、路徑、IP地址和端口:
網站配置完成之後,瀏覽.svc文件,驗證網站是否配置成功,如出現下面的截圖,說明網站配置成功:
七、建立代理類
客戶端引用WCF的時候通常是靜態引用,直接添加服務引用,這種方式若是IP地址和端口號變了,須要用代碼從新編譯而後在部署,這樣不方便。這裏使用svcutil代理類的方式進行客戶端的調用。
使用svcutil生成代理類:
新建一個項目,選擇類庫項目,把剛纔生成的類文件添加到類庫項目中,項目結構以下:
在類庫項目中新添加一個類,命名爲:MyServiceProxy,使用這個類來調用代理類,MyServiceProxy類的定義以下:
1 using Public.ConfigBinding; 2 using System; 3 using System.Collections.Generic; 4 using System.Configuration; 5 using System.Linq; 6 using System.ServiceModel; 7 using System.Text; 8 using System.Threading.Tasks; 9 10 namespace MyProxyService 11 { 12 public class MyServiceProxy 13 { 14 private static MyServiceClient _databaseService; 15 16 private static MyServiceClient DatabaseService 17 { 18 get 19 { 20 if (_databaseService == null) 21 { 22 string sApServer1 = ConfigurationManager.AppSettings["ApServer1"]; 23 24 if (sApServer1 == null) 25 { 26 _databaseService = new MyServiceClient(); 27 } 28 else 29 { 30 EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer1)); 31 _databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr); 32 } 33 } 34 35 if (_databaseService.State == CommunicationState.Faulted) 36 { 37 string sApServer2 = ConfigurationManager.AppSettings["ApServer2"]; 38 39 if (sApServer2 == null) 40 { 41 _databaseService = new MyServiceClient(); 42 } 43 else 44 { 45 EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer2)); 46 _databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr); 47 } 48 } 49 50 return _databaseService; 51 } 52 } 53 54 /// <summary> 55 /// 返回當前時間 56 /// </summary> 57 /// <returns></returns> 58 public static DateTime GetCurrentTime() 59 { 60 return DatabaseService.GetCurrentTime(); 61 } 62 } 63 }
ApServer1和ApServer2是在配置文件中配置的IP地址和端口號,這樣若是IP地址和端口號變了,只須要修改配置文件就能夠。
GetCurrentTime()方法是調用的代理類裏面的方法,把該方法定義爲靜態方法。
八、建立客戶端調用
在解決方案中,新建一個winform程序,界面上面只有一個button按鈕,點擊按鈕,彈出當前時間。須要添加對MyProxyService.dll文件的引用,在配置文件中增長ApServer1和ApServer2兩個節點,配置文件以下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <appSettings> 4 <add key="ApServer1" value="http://127.0.0.1:8090"/> 5 <add key="ApServer2" value="http://127.0.0.1:8090"/> 6 </appSettings> 7 <startup> 8 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> 9 </startup> 10 </configuration>
button按鈕事件代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using MyProxyService; 11 12 namespace WinformClient 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void btn_CurrentTime_Click(object sender, EventArgs e) 22 { 23 DateTime dtNow = MyServiceProxy.GetCurrentTime(); 24 MessageBox.Show("當前時間:" + dtNow.ToString()); 25 } 26 } 27 }
點擊按鈕後,運行結果以下:
4、使用自定義工廠的方式
一、新添加一個WCF服務,命名爲CustomService,把默認生成的CustomService.svc.cs文件刪掉,從新添加一個類:CustomService,該類繼承自生成的ICustomService接口,項目結構以下:
修改CustomService.svc文件:
1 <%@ ServiceHost Service="WcfSvcDemo.CustomService" Factory="Public.CustomService.CustomServiceHostFactory" %>
CustomServiceHostFactory是在另外的Public.dll裏面建立的工廠類,用來返回ServiceHost,Public.dll的項目結構以下:
CustomServiceHost類繼承自ServiceHost類,代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.ServiceModel.Activation; 7 8 namespace Public.CustomService 9 { 10 public class CustomServiceHost :ServiceHost 11 { 12 public CustomServiceHost(Type serviceType, params Uri[] baseAddresses) 13 : base(serviceType, baseAddresses) 14 { 15 //加載Web.config的配置 16 log4net.Config.XmlConfigurator.Configure(); 17 } 18 protected override void ApplyConfiguration() 19 { 20 base.ApplyConfiguration(); 21 } 22 } 23 }
CustomServiceHostFactory是工廠類,繼承自ServiceHostFactory,用來返回ServiceHost,代碼以下;
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.ServiceModel.Activation; 7 8 namespace Public.CustomService 9 { 10 public class CustomServiceHostFactory : ServiceHostFactory 11 { 12 protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 13 { 14 CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses); 15 return customServiceHost; 16 } 17 } 18 }
HttpBinding代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.ServiceModel; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Xml; 8 9 namespace Public.ConfigBinding 10 { 11 public class HttpBinding 12 { 13 private static BasicHttpBinding _BasicHttpBinding; 14 15 public static BasicHttpBinding BasicHttpBinding 16 { 17 get 18 { 19 if (_BasicHttpBinding == null) 20 { 21 _BasicHttpBinding = new BasicHttpBinding(); 22 23 // 接收的訊息大小上限,默認值爲65,536字節, 24 // 目前設定1k * 512,若是資料量大於這個值,請提出討論,ex:8000筆資料大概128k 25 _BasicHttpBinding.MaxReceivedMessageSize = 400 * 8192 * 512; 26 27 // 因爲回傳String長度過長在反串行化時會出錯! 28 // 因此放大最大字符串長度 29 _BasicHttpBinding.ReaderQuotas.MaxStringContentLength = 8192 * 1022; 30 _BasicHttpBinding.ReaderQuotas.MaxArrayLength = 8192 * 1022; 31 _BasicHttpBinding.SendTimeout = new TimeSpan(0, 5, 0); 32 33 } 34 35 return _BasicHttpBinding; 36 } 37 } 38 } 39 }
把CustomService.svc部署到IIS上面、建立代理類的方法、客戶端調用和默認工廠裏面的方法同樣,此處不在描述。
項目代碼下載路徑:https://files.cnblogs.com/files/dotnet261010/WCFStudyDemo.rar