WCF開發實戰
一、 服務器應用開發
(一) 創建“WCF服務庫”
“文件(F)”->“新建項目(P)...”打開新建項目對話框。在左側的“項目類型”中選擇“WCF”,然後再在右側的“模板”中選擇“WCF服務庫”。
在下面的“名稱”文本框中,填寫我們要創建的WCF服務庫的項目名稱“WcfEmployee”。
點擊確定,會創建出我們的WCF服務庫項目,在解決方案中會自動爲我們生成兩個類文件“IService1.cs”和“Service1.cs”。
這兩個類文件是兩個WCF示例文件,對我們開發沒有什麼用處,現在我們刪掉這兩個文件。
創建Employee實體類
在“解決方案窗口”中,我們右擊Services項目名,選擇“添加”,再單擊“類”
在彈出的“添加新項”窗口中,選擇“類”,並在“名稱”文本框中寫入項名稱“Employee.cs”。
爲Employee實體類編寫代碼
using System.Runtime.Serialization;
namespace WcfEmployee
{
[DataContract]
public class Employee
{
[DataMember]
public string EmployeeId;
[DataMember]
public string EmployeeName;
[DataMember]
public int EmployeeAge;
}
}
爲了保證此類在WCF調用中能夠被序列化,我們在Employee類上面加入[DataContract]標籤,在每個需要序列化的成員變量上加入[DataMember]標籤。這兩個標籤在使用的進候需要導入using System.Runtime.Serialization命名空間。
到此爲至,我們創建完了需要在服務中傳輸的複雜的數據類型Employee。
創建服務接口
創建服務接口,聲明對外發布的類和方法。
在“解決方案窗口”中,我們右擊WcfEmployee項目名,選擇“添加”,再單擊“類”
在彈出的“添加新項”窗口中,選擇“類”,並在“名稱”文本框中寫入項名稱“IEmployeeSrv.cs”。
在此類文件中我們編寫服務接口,代碼如下:
using System.Collections.Generic;
using System.ServiceModel;
namespace WcfEmployee
{
[ServiceContract]
public interface IEmployeeSrv
{
[OperationContract]
void AddEmployees(Employee employee);
[OperationContract]
List<Employee> GetAllEmployees();
[OperationContract]
void RemoveEmployee(string id);
}
}
在IEmployeeSrv接口上面,我們定義了[ServiceContract]標籤,此標籤代表此接口及實現此接口的類都是對外發布的Service類,在每個需要對外發布的方法上都加上[OperationContract]標籤,以使外部可以訪問到此方法。
[ServiceContract]和[OperationContract]這兩個標籤需要導入using System.ServiceModel命名空間。
創建實現服務接口的類
實現我們上面聲明的服務接口,實現對Employee的添加、刪除和檢索的具體功能。
在“解決方案窗口”中,我們右擊WcfEmployee項目名,選擇“添加”,再單擊“類”
在彈出的“添加新項”窗口中,選擇“類”,並在“名稱”文本框中寫入項名稱“EmployeeSrv.cs”。
在此類文件中編寫代碼實現IEmployeeSrv.cs服務接口。
using System.Collections.Generic;
using System.ServiceModel;
namespace WcfEmployee
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class EmployeeSrv:IEmployeeSrv
{
List<Employee> _Employee=new List<Employee>();
public void AddEmployees(Employee employee)
{
employee.EmployeeId=Guid.NewGuid().ToString();
_Employee.Add(employee);
}
public void RemoveEmployee(string id)
{
Employee employee=_Employee.Find(p=>p.EmployeeId==id);
_Employee.Remove(employee);
}
public List<Employee> GetAllEmployees()
{
return _Employee;
}
}
}
此類是對IEmployeeSrv接口的具體實現,在此類的上面我們聲明瞭[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]標籤,此標籤代表這個類採用SingleTone(單類模式)來生成對象。
使用[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]接口需要導入using System.ServiceModel;命名空間。
將app.config中“Service1”改爲EmployeeSrv”.
運行WCF進行測試。
在VS2008中爲我們提供了測試WCF的工具,按F5啓動WCF會出現兩個東西
一個是在右下角的托盤圖標中會出現WCFSVCHost(WCF服務主機),它爲我們在開發時候提供了一個運行WCF的服務器,用來爲測試客戶端提供WCF服務。
另一個是“WCF測試客戶端”
“測試客戶端”從WcfSVCHost中取得WCF服務的元數據,解析爲右側的“服務結構樹”,從這裏面我們可以看到此WCF服務爲我們提供了一個服務契約“IEmployeeSrv”,此服務契約中對外提供了三個可調用的方法。
雙擊AddEmployees()方法,我們可以從右面輸入相關的數據然後點擊“調用”,就可以把數據送給WCF服務器,去調用對應的方法了。
(二) 使用IIS發佈WCF服務
上一篇中,我們創建了一個簡單的WCF服務,在測試的時候,我們使用VS2008自帶的WCFSVCHost(WCF服務主機)發佈WCF服務,以便進行測試。這種VS2008內置的WCFSVCHost只適用於開發人員測試的使用,能進行WCF服務部署。這一篇文章中我們來看一下如何在IIS中部發布我們上一篇中做好的WCF服務。
第一步:打開我們上一篇文章中建立的WCF服務項目。
圖《1》
第二步:新建WCF服務站點。在解決方案上右擊,選擇“添加”->“新建網站”,打開新建網站對話框。在“添加新網站”對話框中,我們選擇“WCF服務”,並把網站的名子命名爲“BookServiceHost”
圖《2》
建立起來的新的WCF服務站點的結果如下,其中在App_Code文件中自動爲我們生成兩個類文件:IService.cs和Service.cs。這兩個文件對我們來說沒有用,我們刪掉。
圖《3》
第三步:在剛剛創建的WCF服務站點上添加對WCF服務庫項目--Services項目的引用。
圖《4》
第四步:配置Service.svc文件。
雙擊Service.svc文件,我們可以看到它的聲明指示如下:
<%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>
由於在第二步中我們已經把IService.cs和Service.cs兩個文件已經刪除了,所以這裏的聲明指示內容修改一下,讓這個Service.svc文件的後臺代碼指向我們上次創建的WCF服務庫項目--Services項目中的類,改後的代碼如下:
<%@ServiceHostLanguage="C#"Debug="true"Service="WcfEmployee.EmployeeSrv"%>
我們把其中的Service屬性指定爲WcfEmployee命名空間下的EmployeeSrv類,並把CodeBehind屬性刪去了。
第五步:配置此WCF服務站點與WCF服務庫項目之間的類的對應。
雖然在第三步中我們添加了對Services項目的引用,並且在第四步中修改了Service.svc的類的對應,但此時我們的WCF服務站點並不能把WCF服務庫中的服務和終結點發布出來,還需要我們對web.config進行一系列的配置工作。
在web.config上右擊選擇“編輯WCF配置”
在彈出的服務配置編輯器窗口中,單擊”新建服務...”。
單擊”瀏覽”.定位到本項目下的Bin目錄,單擊WcfEmployee.dll,再選打開.
選”WcfEmployee.EmployeeSrv”,單擊”打開”
在Service.svc上右擊,選擇“在瀏覽器中查看”,在IE中運行此服務。
由此我們看到我們可以在ASP.NET Development Server中發佈我們的WCF服務了。
第八步:在IIS佈署此WCF服務站點。
打開iis管理器,在其中找到一個正常的網站,如下圖中的wa3,右擊之,
別名任意,物理路徑必須準確爲項目所在的目錄.
使瀏覽器指向”http://localhost:83/EmployeeService/service.svc”,注意”EmployeeService”爲剛纔指定的別名.
在外網其他電腦上使瀏覽器指向”http://221.213.51.74:83/EmployeeService/service.svc”
也應該出現上圖.
(三) 創建WCF服務宿主程序
1. 控制檯應用程序作爲宿主程序
建立控制檯應用程序ConsSrvHostEmployee
《圖1》
向ConsSrvHostEmployee程序中添加兩個引用:一個是WCF服務庫Services項目的引用,另一個是System.ServiceModel引用。
生成該項目。
在ConsSrvHostEmployee 項目中的Program.cs中編寫代碼。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WcfEmployee;
using System.ServiceModel;
namespace ConsSrvHostEmployee
{
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(EmployeeSrv));
host.Credentials.ServiceCertificate.SetCertificate("CN=OujianpingServer");
host.Open();
Console.WriteLine("服務已啓動......");
Console.ReadLine();
host.Close();
}
}
}
配置App.Config
在App.Config上右擊選擇選擇“編輯WCF配置”,彈出服務配置管理窗口
第六步:點擊右邊的“新建服務...”彈出“新建服務元素嚮導”窗口,單擊“瀏覽”按鈕,選擇Bin/Debug目錄下Services.dll程序集中的Services.EmployeeSrv服務。
注意進行綁定配置,選擇“NewBinding0 netTcpBinding”。
配置元數據終結點:
//如果沒有這個過程,也可以正常啓動服務,但客戶端無法通過引用”添加服務引用”.
System.ServiceModel
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<services>
<service behaviorConfiguration="NewBehavior0" name="WcfEmployee.EmployeeSrv">
<clear />
<endpoint address="basic" binding="basicHttpBinding" contract="WcfEmployee.IEmployeeSrv"
listenUriMode="Explicit">
<identity>
<certificateReference storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectName" findValue="OujianpingServer" />
</identity>
</endpoint>
<endpoint address="net.tcp://localhost:8082/Service" binding="netTcpBinding"
bindingConfiguration="NewBinding0" contract="WcfEmployee.IEmployeeSrv"
listenUriMode="Explicit">
<identity>
<certificateReference storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectName" findValue="OujianpingServer" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/service" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior0">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="NewBinding0">
<security>
<transport clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
注:今後可將以上文件當作一個模版來使用,比如新建了個了名爲wcflib的wcf服務庫項目,在此項目中建立了一個名爲wcfserver的服務,並使用名爲myCert的證書,則可將文中的WcfEmployee替換爲wcflib,EmployeeSrv替換爲wcfserver,OujianpingServer替換爲myCert即可,而不必進行上述繁瑣的操作。
用以下命令創建證書
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=OuJianpingServer -sky exchange -pe
(注:複製上述命令時須先整行顯示,且只能不能複製行尾的多餘空格,否則無法執行.
如下圖
安裝的證書可在控制檯(cmd mmc)的本地計算機證書下看到
(文件-添加/刪除管理單位-證書-計算機帳戶-本地計算機)
測試一下。
選按Ctrl+F5運行打開瀏覽器,
2. Windows服務作爲宿主程序
建立Windows服務程序WinSrvEmployee
注意:必須選.Net Framework 4.5,與WCF服務庫項目保持一致,否則引入WCF服務庫會出現問題。
《圖2》
第二步:WinSrvEmployee程序中添加兩個引用:一個是WCF服務庫Services項目的引用,另一個是System.ServiceModel引用。
第三步:修改Service1的屬性
在Service1的設計界面中右擊,選擇“屬性”,把其中的(Name)和ServiceName都改爲EmployeeSrv
《圖3》
第四步:在Service1中編寫代碼如下
《圖4》
把ConsSrvHostEmployeev項目中的App.Config複製過來.爲防止元數據泄漏,應將其中以下內容刪除:
<endpointaddress="mex"binding="mexHttpBinding"bindingConfiguration=""
contract="IMetadataExchange" />
到這裏我們已經作好一個可以發佈EmployeeSrv服務的Windows Service宿主程序了。
下面我們要看一看如何把這個Windows Service運行起來。
第六步:爲服務添加安裝程序。
在Service1設計界面中右擊,選擇“添加安裝程序”
《圖6》
生成安裝程序組件,出現界面如下
《圖7》
serviceProcessInstaller1:服務安裝的信息
《圖8》
serviceInstaller1:服務本身的信息
《圖9》
生成項目
安裝的服務
在計算機中搜索installutil.exe文件,找到版本最高的那個,將其路徑寫入環量變量path中。
打開VS2008命令窗口,進入當前項目的bin/Debug文件夾中,執行命令 :installutil WinSrvEmployee.exe
(注:卸載時 用InstallUtil.exe /u WinSrvEmployee.exe。)
啓動EmployeeSrv服務
打開服務管理器,我們可以看到我們剛剛註冊上的服務已經存在了,但還沒有啓動,右擊“啓動”
注意:上述通過windows server項目發佈在.net 3.5 、vs 2008和windows server 2003的環境下無法通過,表現爲啓動服務會出錯,會出現以下提示
,原因不明,但可以通過構建winform項目來代替,尤其是托盤型的winform應用。
二、 部署WCF服務器
三、 客戶端程序開發
(一) 創建WCF客戶端程序
(此步vs2008不必做)
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 System.ServiceModel;
using System.ServiceModel.Description;
namespace EmployeeIMS
{
publicpartialclassForm1 : Form
{
ServiceReference1.EmployeeSrvClient mClient = new ServiceReference1.EmployeeSrvClient("NetTcpBinding_IEmployeeSrv");
public Form1()
{
InitializeComponent();
}
privatevoid Form1_Load(object sender, EventArgs e)
{
mClient.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
ServiceReference1.Employee employee = new ServiceReference1.Employee();
employee.EmployeeName = "王芳";
employee.EmployeeAge = 18;
mClient.AddEmployees(employee);
employee.EmployeeName = "李明";
employee.EmployeeAge = 17;
mClient.AddEmployees(employee);
string msg = "";
foreach(ServiceReference1.Employee em in mClient.GetAllEmployees())
{
msg += em.EmployeeName + "" + em.EmployeeAge + "\r";
}
MessageBox.Show(msg);
}
}
}
(二) 修改app.config
Localhost
將app.config文件中的localhost替換爲WCF服務器的實際ip地址,否則在其他計算機上運行時無法連接到服務器。
SendTimeout
如果客戶端的請求需要服務花較長的時間才能回覆,且這個過程會超過1分鐘,則需要改變使用了進行數據傳輸的綁定(如netTcpBinding)的SendTimeout的值,並應該留有餘量,否則超過1分鐘後,客戶端將拋出超時的錯誤。
maxBufferPoolSize和maxBufferSize
如果傳輸的數據過大,則需要改變使用了進行數據傳輸的綁定(如netTcpBinding)的maxBufferPoolSize和maxBufferSize的值,否則,客戶端會拋出錯誤。