詳解WCF服務(wù)中的svc文件
一、新建一個(gè)WCF服務(wù)應(yīng)用程序:
文件->新建->項(xiàng)目:選擇WCF下面的WCF服務(wù)應(yīng)用程序

二、分析WcfSvcDemo項(xiàng)目,該項(xiàng)目的結(jié)構(gòu)如下:

在該項(xiàng)目中,會(huì)默認(rèn)生成一個(gè)IService1.cs的文件和Service1.svc文件。Service1.svc文件封裝的就是提供給客戶端的服務(wù)引用。
首先查看IService1.cs文件,從名字上面就可以看出這是一個(gè)接口文件,里面定義了一個(gè)接口IService1,接口上面使用了ServiceContract,意思是把這個(gè)接口聲明為服務(wù)契約,服務(wù)契約是對(duì)客戶端而言的,就是這個(gè)接口可以暴露出來(lái)讓客戶端可以看見(jiàn)。接口里面定義了兩個(gè)方法,每個(gè)方法上面都使用了[OperationContract],意思是把這兩個(gè)方法聲明為操作契約。只有把接口里面的方法聲明為操作契約,在客戶端里面才可以看到相應(yīng)的方法,否則在客戶端里面看不到在接口里面定義的方法。
在來(lái)看Service.svc文件,可以看到下面有一個(gè)Service.svc.cs文件,這個(gè)文件里面定義了一個(gè)繼承IService1接口的類(lèi)Service1,并實(shí)現(xiàn)了IService1接口里面的方法。
刪除Service.svc.cs文件,可以查看Service.svc文件,該文件里面就一行代碼;
<%@ ServiceHost Language="C#" Debug="true" Service="WcfSvcDemo.Service1" CodeBehind="Service1.svc.cs" %>
這里面有兩個(gè)重要的參數(shù):Service和CodeBehind。Service是屬性值是WCF的服務(wù)實(shí)現(xiàn)類(lèi)的完全限定名。CodeBehind是服務(wù)實(shí)現(xiàn)類(lèi)所在的文件名。在運(yùn)行的時(shí)候,宿主程序從svc文件中的Service屬性得到WCF服務(wù)的完全限定名,然后從配置文件中找到同名的servicce,進(jìn)而找到所有的EndPoint,并根據(jù)其屬性進(jìn)行實(shí)例化。
配置文件中的Service名字必須是Service類(lèi)名的完全限定名(即Namespace.classname),EndPoint的Contract必須是Service接口的完全限定名。否則,程序就無(wú)法從程序集中找到相應(yīng)的類(lèi)進(jìn)行加載。
注意:如果要修改接口實(shí)現(xiàn)類(lèi)的名稱,必須使用“重構(gòu)”的方式進(jìn)行修改,因?yàn)橹挥欣?ldquo;重構(gòu)”的方式修改Servie類(lèi)名的時(shí)候,.svc文件里面Service的屬性值才會(huì)被修改,利用其它方式修改類(lèi)名,.svc文件里面Service的屬性值會(huì)保留原值,這樣在運(yùn)行的時(shí)候,根據(jù)svc里面Service的屬性值查找不到相應(yīng)的類(lèi),程序就會(huì)報(bào)錯(cuò)。
svc文件里面還有一個(gè)重要的參數(shù):ServiceHostFactory。ServiceHostFactory旨在解決從IIS或WAS中訪問(wèn)自定義ServiceHost的問(wèn)題。因?yàn)閺腟erviceHost派生的自定義宿主是動(dòng)態(tài)配置的并且可能為各種類(lèi)型,所以宿主環(huán)境從不會(huì)直接將其實(shí)例化。相反,WCF使用工廠模式提供宿主環(huán)境和服務(wù)的具體類(lèi)型之間的間接層。除非進(jìn)行通知,否則它使用返回ServiceHost的實(shí)例的ServiceHostFactory的默認(rèn)實(shí)現(xiàn)。(在新建的svc文件中默認(rèn)實(shí)現(xiàn)就是CodeBehind屬性的值)。但也可以通過(guò)在@ServiceHost指令中指定工廠實(shí)現(xiàn)的CLR類(lèi)型名稱來(lái)提供自己的工廠(用于返回派生宿主)。
下面是用于返回派生的ServiceHost的自定義ServiceHostFactory:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
namespace Public.CustomService
{
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses);
return customServiceHost;
}
}
}其中CustomServiceHost是自定義的繼承自ServiceHost的類(lèi),用于讀取配置文件的配置,CustomServiceHost類(lèi)的定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
namespace Public.CustomService
{
public class CustomServiceHost :ServiceHost
{
public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
//加載Web.config的配置
log4net.Config.XmlConfigurator.Configure();
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
}
}若要使用此工廠,而不使用默認(rèn)工廠,則應(yīng)該在@ServiceHost指令中提供相應(yīng)的類(lèi)型名稱:
<%@ ServiceHost Service="CustomSvcDemo.DatabaseService" Factory="Public.CustomService.CustomServiceHostFactory" %>
其中Service是實(shí)現(xiàn)類(lèi)的完全限定名,F(xiàn)actory是自定義ServiceHostFactory的完全限定名,Public是一個(gè)dll文件。
若要使用此工廠,而不使用默認(rèn)工廠,則應(yīng)該在@ServiceHost指令中提供相應(yīng)的類(lèi)型名稱:
盡管對(duì)于從CreateServiceHost返回的ServiceHost可以執(zhí)行什么操作沒(méi)有技術(shù)限制,但建議您盡可能使工廠實(shí)現(xiàn)簡(jiǎn)單化。如果有大量的自定義邏輯,最好將這些邏輯放入宿主內(nèi)而不是工廠內(nèi),以便可以重用它們。
應(yīng)在這里提及另一個(gè)承載API的層。WCF還具有ServiceHostBase和ServiceHostFactoryBase,可從中分別派生ServiceHost和ServiceHostFactory。對(duì)于您必須通過(guò)自己的自定義創(chuàng)建來(lái)交換元數(shù)據(jù)系統(tǒng)的大型組件的更高級(jí)方案,存在上述這些特性。
下面通過(guò)兩個(gè)具體的示例程序分別實(shí)現(xiàn)上面描述的默認(rèn)工廠和自定義工廠。
三、使用默認(rèn)工廠方式
1、刪除新建項(xiàng)目時(shí)自動(dòng)創(chuàng)建的IService1.cs和Service1.svc文件,然后添加一個(gè)svc文件,在項(xiàng)目上面右鍵->添加->新建項(xiàng):

2、在新建項(xiàng)里面選擇web里面的WCF服務(wù),命名為MyService:

3、點(diǎn)“添加”,除了創(chuàng)建MyService.svc文件以外,還會(huì)自動(dòng)創(chuàng)建一個(gè)名為IMyService.cs的接口文件,MyService.svc.cs里面的MyService默認(rèn)實(shí)現(xiàn)IMyService接口.
刪除IMyService接口里面自動(dòng)生成的方法,添加一個(gè)GetCurrentTime的方法,用來(lái)返回當(dāng)前的時(shí)間,IMyService接口定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfSvcDemo
{
// 注意: 使用“重構(gòu)”菜單上的“重命名”命令,可以同時(shí)更改代碼和配置文件中的接口名“IMyService”。
[ServiceContract]
public interface IMyService
{
/// <summary>
/// 獲取當(dāng)前時(shí)間
/// </summary>
/// <returns></returns>
[OperationContract]
DateTime GetCurrentTime();
}
}4、MyService.svc.cs里面的MyService類(lèi)實(shí)現(xiàn)IMyService接口,MyService類(lèi)定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfSvcDemo
{
// 注意: 使用“重構(gòu)”菜單上的“重命名”命令,可以同時(shí)更改代碼、svc 和配置文件中的類(lèi)名“MyService”。
// 注意: 為了啟動(dòng) WCF 測(cè)試客戶端以測(cè)試此服務(wù),請(qǐng)?jiān)诮鉀Q方案資源管理器中選擇 MyService.svc 或 MyService.svc.cs,然后開(kāi)始調(diào)試。
public class MyService : IMyService
{
/// <summary>
/// 返回當(dāng)前時(shí)間
/// </summary>
/// <returns></returns>
public DateTime GetCurrentTime()
{
return DateTime.Now;
}
}
}5、修改配置文件,增加service、binding等節(jié)點(diǎn),修改后的配置文件如下:
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<httpRuntime/>
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="metadataBehavior" name="WcfSvcDemo.MyService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="Contract" name="MyService" contract="WcfSvcDemo.IMyService"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<!-- 為避免泄漏元數(shù)據(jù)信息,請(qǐng)?jiān)诓渴鹎皩⒁韵轮翟O(shè)置為 false -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- 要接收故障異常詳細(xì)信息以進(jìn)行調(diào)試,請(qǐng)將以下值設(shè)置為 true。在部署前設(shè)置為 false 以避免泄漏異常信息 -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<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">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
</binding>
</basicHttpBinding>
</bindings>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
若要在調(diào)試過(guò)程中瀏覽 Web 應(yīng)用程序根目錄,請(qǐng)將下面的值設(shè)置為 True。
在部署之前將該值設(shè)置為 False 可避免泄露 Web 應(yīng)用程序文件夾信息。
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>主要是修改service節(jié)點(diǎn)里面的name是服務(wù)實(shí)現(xiàn)類(lèi)的完全限定名,contract是服務(wù)接口的完全限定名。
6、把WCF服務(wù)部署到IIS上面
在IIS上面網(wǎng)站->添加網(wǎng)站:

配置網(wǎng)站名稱、路徑、IP地址和端口:

網(wǎng)站配置完成以后,瀏覽.svc文件,驗(yàn)證網(wǎng)站是否配置成功,如出現(xiàn)下面的截圖,說(shuō)明網(wǎng)站配置成功:

7、創(chuàng)建代理類(lèi)
客戶端引用WCF的時(shí)候一般是靜態(tài)引用,直接添加服務(wù)引用,這種方式如果IP地址和端口號(hào)變了,需要用代碼重新編譯然后在部署,這樣不方便。這里使用svcutil代理類(lèi)的方式進(jìn)行客戶端的調(diào)用。
使用svcutil生成代理類(lèi):

新建一個(gè)項(xiàng)目,選擇類(lèi)庫(kù)項(xiàng)目,把剛才生成的類(lèi)文件添加到類(lèi)庫(kù)項(xiàng)目中,項(xiàng)目結(jié)構(gòu)如下:

在類(lèi)庫(kù)項(xiàng)目中新添加一個(gè)類(lèi),命名為:MyServiceProxy,使用這個(gè)類(lèi)來(lái)調(diào)用代理類(lèi),MyServiceProxy類(lèi)的定義如下:
using Public.ConfigBinding;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace MyProxyService
{
public class MyServiceProxy
{
private static MyServiceClient _databaseService;
private static MyServiceClient DatabaseService
{
get
{
if (_databaseService == null)
{
string sApServer1 = ConfigurationManager.AppSettings["ApServer1"];
if (sApServer1 == null)
{
_databaseService = new MyServiceClient();
}
else
{
EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer1));
_databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr);
}
}
if (_databaseService.State == CommunicationState.Faulted)
{
string sApServer2 = ConfigurationManager.AppSettings["ApServer2"];
if (sApServer2 == null)
{
_databaseService = new MyServiceClient();
}
else
{
EndpointAddress endPointAddr = new EndpointAddress(string.Format("{0}/MyService.svc", sApServer2));
_databaseService = new MyServiceClient(HttpBinding.BasicHttpBinding, endPointAddr);
}
}
return _databaseService;
}
}
/// <summary>
/// 返回當(dāng)前時(shí)間
/// </summary>
/// <returns></returns>
public static DateTime GetCurrentTime()
{
return DatabaseService.GetCurrentTime();
}
}
}ApServer1和ApServer2是在配置文件中配置的IP地址和端口號(hào),這樣如果IP地址和端口號(hào)變了,只需要修改配置文件就可以。
GetCurrentTime()方法是調(diào)用的代理類(lèi)里面的方法,把該方法定義為靜態(tài)方法。
8、創(chuàng)建客戶端調(diào)用
在解決方案中,新建一個(gè)winform程序,界面上面只有一個(gè)button按鈕,點(diǎn)擊按鈕,彈出當(dāng)前時(shí)間。需要添加對(duì)MyProxyService.dll文件的引用,在配置文件中增加ApServer1和ApServer2兩個(gè)節(jié)點(diǎn),配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="ApServer1" value="http://127.0.0.1:8090"/>
<add key="ApServer2" value="http://127.0.0.1:8090"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>button按鈕事件代碼如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MyProxyService;
namespace WinformClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btn_CurrentTime_Click(object sender, EventArgs e)
{
DateTime dtNow = MyServiceProxy.GetCurrentTime();
MessageBox.Show("當(dāng)前時(shí)間:" + dtNow.ToString());
}
}
}點(diǎn)擊按鈕后,運(yùn)行結(jié)果如下:

四、使用自定義工廠的方式
1、新添加一個(gè)WCF服務(wù),命名為CustomService,把默認(rèn)生成的CustomService.svc.cs文件刪掉,重新添加一個(gè)類(lèi):CustomService,該類(lèi)繼承自生成的ICustomService接口,項(xiàng)目結(jié)構(gòu)如下:

修改CustomService.svc文件:
<%@ ServiceHost Service="WcfSvcDemo.CustomService" Factory="Public.CustomService.CustomServiceHostFactory" %>
CustomServiceHostFactory是在另外的Public.dll里面創(chuàng)建的工廠類(lèi),用來(lái)返回ServiceHost,Public.dll的項(xiàng)目結(jié)構(gòu)如下:

CustomServiceHost類(lèi)繼承自ServiceHost類(lèi),代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
namespace Public.CustomService
{
public class CustomServiceHost :ServiceHost
{
public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
//加載Web.config的配置
log4net.Config.XmlConfigurator.Configure();
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
}
}CustomServiceHostFactory是工廠類(lèi),繼承自ServiceHostFactory,用來(lái)返回ServiceHost,代碼如下;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
namespace Public.CustomService
{
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses);
return customServiceHost;
}
}
}HttpBinding代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace Public.ConfigBinding
{
public class HttpBinding
{
private static BasicHttpBinding _BasicHttpBinding;
public static BasicHttpBinding BasicHttpBinding
{
get
{
if (_BasicHttpBinding == null)
{
_BasicHttpBinding = new BasicHttpBinding();
// 接收的訊息大小上限,默認(rèn)值為65,536字節(jié),
// 目前設(shè)定1k * 512,如果資料量大于這個(gè)值,請(qǐng)?zhí)岢鲇懻?,ex:8000筆資料大概128k
_BasicHttpBinding.MaxReceivedMessageSize = 400 * 8192 * 512;
// 由于回傳String長(zhǎng)度過(guò)長(zhǎng)在反串行化時(shí)會(huì)出錯(cuò)!
// 所以放大最大字符串長(zhǎng)度
_BasicHttpBinding.ReaderQuotas.MaxStringContentLength = 8192 * 1022;
_BasicHttpBinding.ReaderQuotas.MaxArrayLength = 8192 * 1022;
_BasicHttpBinding.SendTimeout = new TimeSpan(0, 5, 0);
}
return _BasicHttpBinding;
}
}
}
}把CustomService.svc部署到IIS上面、創(chuàng)建代理類(lèi)的方法、客戶端調(diào)用和默認(rèn)工廠里面的方法一樣,此處不在描述。
項(xiàng)目代碼下載路徑:下載地址
到此這篇關(guān)于WCF服務(wù)中svc文件詳解的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C# listview添加combobox到單元格的實(shí)現(xiàn)代碼
從別處轉(zhuǎn)來(lái)的,自己進(jìn)行了一些小的修改,還不錯(cuò),你自己先拖一個(gè)ListView1和一個(gè)ComboBox1,需要的朋友可以參考下2014-06-06
C#使用System.Net郵件發(fā)送功能踩過(guò)的坑
這篇文章主要介紹了C#使用System.Net郵件發(fā)送功能踩過(guò)的坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
c#通過(guò)unicode編碼判斷字符是否為中文示例分享
本文介紹了c#通過(guò)unicode編碼判斷字符是否為中文的示例,在unicode字符串中,中文的范圍是在4E00..9FFF:CJK Unified Ideographs。通過(guò)對(duì)字符的unicode編碼進(jìn)行判斷來(lái)確定字符是否為中文2014-01-01
在Framework 4.0中:找出新增的方法與新增的類(lèi)(二)
為什么動(dòng)態(tài)加載程序集無(wú)法找出Framework 4.0 和Framwork2.0 新增的方法和類(lèi)2013-05-05
WPF自定義控件實(shí)現(xiàn)ItemsControl魚(yú)眼效果
這篇文章主要為大家詳細(xì)介紹了WPF如何通過(guò)自定義控件實(shí)現(xiàn)ItemsControl魚(yú)眼效果,文中的示例代碼講解詳細(xì),需要的可以參考一下2024-01-01

