開始前先貼出一張最終的項目結構圖:
1.創建新的 Visual Studio控制檯應用程序名爲項目服務。
2.添加對下列程序集的引用:
(1)System.ServiceModel.dll
(2)System.ServiceProcess.dll
(3)System.Configuration.Install.dll
注:其中(1)(2)是編寫WCF程序必須的dll (3)是發佈Windows服務所需的dll
3.創建類庫Contracts(這是我測試起的嗎,你可自己起),並創建ICalculator接口用於編寫wcf契約程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace Contracts
{
[ServiceContract(Name = "CalculatorService", Namespace = "http://www.artech.com/")]
public interface ICalculator
{
[OperationContract]
void Add(double x, double y);
[OperationContract]
double Subtract(double x, double y);
[OperationContract]
double Multiply(double x, double y);
[OperationContract]
double Divide(double x, double y);
}
}
4.創建Service類庫,用於編寫對鍥約的實現代碼,即wcf術語中的服務:
using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace Services
{
public class CalculatorService : ICalculator
{
public void Add(double x, double y)
{
double result= x + y;
}
public double Divide(double x, double y)
{
return x - y;
}
public double Multiply(double x, double y)
{
return x * y;
}
public double Subtract(double x, double y)
{
return x / y;
}
}
}
好了,現在我們已經創建好了契約和服務的代碼,要讓wcf程序能夠run起來,能夠被遠程所調用,增需要我們的Windows服務模塊出場了,WindowsService模塊負責制定wcf程序的綁定、終結點等以及負責wcf的宿主。
5.創建Windows服務項目,在WindowsService類庫的app.config中添加如下配置(部分註釋見配置代碼):
<system.serviceModel>
<services>
<service behaviorConfiguration="CalculatorServiceBehavior" name="Services.CalculatorService">
<!--創建終結點,指定終結點的通信方式(這裏是TCP的方式),以及契約的路徑-->
<endpoint address="" binding="netTcpBinding" bindingConfiguration="" contract="Contracts.ICalculator"></endpoint>
<host>
<baseAddresses>
<!--指定wcf服務的請求路徑,該路徑在客戶端調用時使用-->
<add baseAddress="net.tcp://127.0.0.1:9000/CalculatorService.svc"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<!--指定wcf服務元數據請求路徑-->
<serviceMetadata httpGetEnabled="True" httpGetUrl="http://localhost:8000/ServiceModelSamples/service" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" minFreeMemoryPercentageToActivateService="0" />
<bindings>
<netTcpBinding>
<binding name="defaultBinding" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="None">
<message clientCredentialType="None"/>
<transport clientCredentialType="None"></transport>
</security>
<readerQuotas />
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
6.創建CalculatorWindowsService.cs類,並繼承ServiceBase,重寫Onstart和OnStop方法,該類的作用是在Windows服務啓動時負責對wcf程序的宿主:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService
{
public class CalculatorWindowsService : ServiceBase
{
ServiceHost host = new ServiceHost(typeof(Services.CalculatorService));
public CalculatorWindowsService()
{
// Name the Windows Service
ServiceName = "WCFWindowsService";
}
/// <summary>
/// 應用程序的主入口點。
/// </summary>
public static void Main()
{
ServiceBase.Run(new CalculatorWindowsService());
}
protected override void OnStart(string[] args)
{
try
{
host.Open();
}
catch (Exception ex)
{
}
}
protected override void OnStop()
{
host.Close();
}
}
}
現在可以說是完事具備,只欠東風了,而這個東風就是安裝啓動Windows服務的程序,爲了能夠使用Installutil.exe對我們的服務進行安裝,需要在WindowsService下創建ProjectInstaller類:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService
{
// Provide the ProjectInstaller class which allows
// the service to be installed by the Installutil.exe tool
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
private ServiceProcessInstaller process;
private ServiceInstaller service;
public ProjectInstaller()
{
process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
service = new ServiceInstaller();
service.ServiceName = "WCFWindowsService";
Installers.Add(process);
Installers.Add(service);
}
}
}
7.編寫用於安裝、卸載我們Windows服務的批處理文件:
安裝:
@echo off
set /p var=是否要安裝WCF服務(Y/N):
if "%var%" == "y" (goto install) else (goto batexit)
:install
copy C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe InstallUtil.exe /Y
call InstallUtil.exe D:\vsWorkspace\WcfStudyCenter\WcfStudyCenter\WindowsService\bin\Debug\WindowsService.exe
call sc start "WCFWindowsService"
pause
:batexit
exit
注:運行bat時,記得以管理員方式運行
運行成功後的界面:
卸載:
@echo off
set /p var=是否要卸載 WCF服務(Y/N):
if "%var%" == "y" (goto uninstall) else (goto batexit)
:uninstall
call sc stop "WCFWindowsService"
call sc delete "WCFWindowsService"
pause
:batexit
exit
8.將我們的WindowsService安裝並啓動成功後,就可以在客戶端遠程調用我們的wcf程序了:
調用方式一:
創建自己的項目程序,右鍵引用--->添加服務引用---->在地址框輸入元數據地址
點擊確定vs就會替我們生成代理方法,直接引入命名空間調用就可以了。
個人覺得方式一在平時個人玩玩可以使用,但是在正式開發,比如說想要基於wcf搭建分佈系統、微服務等等不太適合,那就可那第二種調用方式。
調用方式二:
生成Contracts類庫的dll,將Contracts.dll引入自己的客戶端程序中,基於ChannelFactory生成代理程序,以下是編寫的生成代理累的簡單工廠程序,只需要指定契約接口就行:
public class WcfServiceProxy<T>
{
public static T CreatServiceProxy()
{
var factory = new ChannelFactory<ICalculator>(new NetTcpBinding(), new EndpointAddress("net.tcp://127.0.0.1:9000/CalculatorService.svc"));
var proxy = factory.CreateChannel();
return (T)proxy;
}
}
調用實例如下:
public static void Main() {
var proxy = WcfServiceProxy<ICalculator>.CreatServiceProxy();
using (proxy as IDisposable)
{
Console.WriteLine(proxy.Multiply(2,3));
}
Console.ReadKey();
}
調用方式三(推薦):
using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading.Tasks;
namespace CSharpStudyCenter
{
public class WcfClent : System.ServiceModel.ClientBase<ICalculator>, ICalculator
{
public WcfClent(Binding binding, EndpointAddress endpointAddress):base(binding,endpointAddress)
{
}
public void Add(double x, double y)
{
base.Channel.Add(x,y);
}
public double Divide(double x, double y)
{
return base.Channel.Divide(x,y);
}
public double Multiply(double x, double y)
{
return base.Channel.Multiply(x,y);
}
public double Subtract(double x, double y)
{
return base.Channel.Subtract(x,y);
}
}
}