若要公开WCF服务,需要提供一个运行服务的宿主环境。就像.NET CLR需要创建宿主环境以托管代码一般,WCF的宿主环境同样运行在进程的应用程序域中。在应用程序域中可以创建一个或多个ServiceHost实例,其关系如图一所示:

图一 托管ServiceHost
WCF并不推荐在应用程序域中创建多个ServiceHost实例。如果要托管多个服务,完全可以在一个宿主中通过多个Endpoint公开多个WCF服务。由于应用程序域对安全进行了隔离,如果需要提供不同的安全上下文,则有必要创建多个ServiceHost实例。
WCF的典型宿主包括以下四种:
1、"Self-Hosting" in a Managed Application(自托管宿主)
2、Managed Windows Services(Windows Services宿主)
3、Internet Information Services(IIS宿主)
4、Windows Process Activation Service(WAS宿主)
以下将通过一个具体的实例分别介绍这几种宿主的托管方式及其相关的注意事项。在这样的一个实例中,我们定义了如下的服务契约:
| 以下是引用片段: namespace BruceZhang.WCF.DocumentsExplorerServiceContract { [ServiceContract] public interface IDocumentsExplorerService { [OperationContract] [FaultContract(typeof(DirectoryNotFoundException))] DocumentList FetchDocuments(string homeDir); [OperationContract] Stream TransferDocument(Document document); } } |
| 以下是引用片段: namespace BruceZhang.WCF.DocumentsExplorerServiceImplementation { [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class DocumentsExplorerService : IDocumentsExplorerService { #region IDocumentsExplorerService Members public DocumentList FetchDocuments(string homeDir) { //implementation code } public Stream TransferDocument(Document document) { //implementation code } #endregion } } |
| 以下是引用片段: namespace BruceZhang.WCF.DocumentsExplorerDataContract { [DataContract] public class Document { //DataMembers } } namespace BruceZhang.WCF.DocumentsExplorerDataContract { [KnownType(typeof(Document))] [CollectionDataContract] public class DocumentList:IList { //IList Methods } } |
1、自托管宿主
利用WCF提供的ServiceHost
| 以下是引用片段: using (ServiceHost host = new ServiceHost(typeof(DocumentsExplorerService))) { host.Open(); Console.WriteLine("The Service had been launched."); Console.Read(); } |
相应地,我们需要配置应用程序的app.config配置文件:
| 以下是引用片段: <configuration> <system.serviceModel> <services> <service name="BruceZhang.WCF.DocumentsExplorerServiceImplementation.DocumentsExplorerService" behaviorConfiguration="DocumentExplorerServiceBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8008/DocumentExplorerService"/> </baseAddresses> </host> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="DocumentExplorerServiceBinding" contract="BruceZhang.WCF.DocumentsExplorerServiceContract.IDocumentsExplorerService"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <bindings> <basicHttpBinding> <binding name="DocumentExplorerServiceBinding" sendTimeout="00:10:00" transferMode="Streamed" messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="9223372036854775807"> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="DocumentExplorerServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration> |
此时,调用服务的客户端配置文件也应与服务的配置保持一致:
| 以下是引用片段: <configuration> <system.serviceModel> <client> <endpoint address="http://localhost:8008/DocumentExplorerService" binding="basicHttpBinding" bindingConfiguration="DocumentExplorerServiceBinding" contract="IDocumentsExplorerService"/> </client> <bindings> <basicHttpBinding> <binding name="DocumentExplorerServiceBinding" sendTimeout="00:10:00" transferMode="Streamed" messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="9223372036854775807"> </binding> </basicHttpBinding> </bindings> </system.serviceModel> </configuration> |
在通常的企业应用中,我们很少会采用自宿主方式托管服务,这是因为这种方式必须要在应用程序运行下,客户端才能够调用服务,且并不便于随时启动和停止服务。除了不具有易用性与易管理性之外,在可靠性、性能等诸多方面受到很多限制。但由于它简单、易于实现,因而往往用于开发期间的调试或演示环境。
自托管宿主支持所有的绑定。
2、Windows Services宿主
Windows Services宿主则完全克服了自托管宿主的缺点,它便于管理者方便地启动或停止服务,且在服务出现故障之后,能够重新启动服务。我们还可以通过Service Control Manager(服务控制管理器),将服务设置为自动启动方式,省去了服务的管理工作。此外,Windows Services自身还提供了一定的安全性以及检测机制和日志机制。
Windows Services宿主的实现也非常简单。我们可以在Visual Studio中创建Windows Services项目。在创建项目之后,就可以创建一个继承了System.ServiceProcess.ServiceBase类的Windows服务类。Windows服务类继承了ServiceBase类的OnStart()和OnStop()方法,完成Windows服务的启动与停止。我们可以重写这两个方法,将ServiceHost的启动与关闭对应地放入这两个方法的实现中。例如我们创建的DocumentsExplorerWindowsService类:
| 以下是引用片段: namespace BruceZhang.WCF.DocumentsExplorer { public partial class DocumentsExplorerWindowsService : ServiceBase { private ServiceHost m_serviceHost = null; public static void Main() { ServiceBase.Run(new DocumentsExplorerWindowsService()); } public DocumentsExplorerWindowsService() { InitializeComponent(); ServiceName = "DocumentsExplorerService"; } protected override void OnStart(string[] args) { if (m_serviceHost != null) { m_serviceHost.Close(); } m_serviceHost = new ServiceHost(typeof(DocumentsExplorerService)); m_serviceHost.Open(); } protected override void OnStop() { if (m_serviceHost != null) { m_serviceHost.Close(); m_serviceHost = null; } } } } |
为了完成ServiceHost实例的创建,我们同样需要在项目中添加app.config配置文件,配置文件的内容与前完全一样。
如果在企业应用中要使用WCF技术,最佳的宿主方式我认为就是Windows Services,尤其是服务器的操作系统不是Vista的情况之下。它便于服务的管理,能够维持服务长时期的运行,同时它还支持所有的绑定,因而受到的限制最小。然而,这种方式唯一的缺点却是对宿主的部署相对比较复杂,必须通过.NET提供的Installutil.exe工具完成对服务宿主的安装(也可以通过安装包的自定义操作完成)。
若要完成对服务宿主的安装,我们还需要创建它的安装程序。我们可以自定义一个安装类,使其继承自System.Configuration.Install.Installer类。更简单的办法则是通过Windows服务提供的设计时支持,直接创建安装类。方法是在Windows服务例如DocumentsExplorerWindowsService的设计器视图下,通过单击右键,在快捷菜单中选择“Add Installer”,如图二所示:
图二 添加安装程序
创建的安装程序ExplorerServiceInstaller如下所示:
| 以下是引用片段: namespace BruceZhang.WCF.DocumentsExplorer { //It needs be ran at the command mode //Type installutil filename to install the windows service //Type services.msc to access the Service Control Manager(SCM) and browse the windows services //Type installutil /u filename to uninstall the windows service [RunInstaller(true)] public partial class ExplorerServiceInstaller : Installer { private ServiceProcessInstaller m_process; private ServiceInstaller m_service; public ExplorerServiceInstaller() { InitializeComponent(); m_process = new ServiceProcessInstaller(); m_process.Account = ServiceAccount.LocalSystem; m_service = new ServiceInstaller(); m_service.ServiceName = "DocumentsExplorerService"; Installers.Add(m_process); Installers.Add(m_service); } } } |
对于安装程序而言,也可以直接在设计器视图下设置它的属性。
安装程序直接建立在Windows服务的程序集中,编译之后会获得一个exe文件,例如DocumentsExplorer.exe。然后,我们通过在Visual Studio的Command Prompt模式下运行如下命令:
installutil DocumentsExplorer.exe
即可完成对服务宿主的安装。
打开服务控制管理器(可以在Command Prompt模式下输入Services.msc打开),可以看到名为DocumentsExplorerService的服务:
图三 服务控制管理器
如果要卸载该服务宿主,可以通过installutil的/u开关卸载。
在企业应用中,我们往往会将该Windows服务设置为自动启动,可以简化管理员的工作。
3、IIS宿主(说明,这里讲的IIS为IIS 6.0)
若要使用IIS宿主,需要为程序集中添加一个svc文件。我们可以通过为项目添加一个新项的方式添加svc文件:
图四 添加svc文件
我们也可以直接创建一个WCF Service应用程序作为IIS宿主,它会自动创建一个svc文件,如图五所示:
图五 创建WCF Service应用程序
创建的svc文件如图:

图六 创建的svc文件
WCF Service应用程序创建的svc文件以及通过添加新项获得svc文件,自动会创建WCF服务。因此,如果我们希望在svc文件中嵌入WCF服务的代码,则可以采取这种方式。例如:
<%@ ServiceHost Language="C#" Debug="true" Service="BruceZhang.WCF.DocumentsExplorerServiceImplementation.DocumentsExplorerService"%>
| 以下是引用片段: using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.IO; using BruceZhang.WCF.DocumentsExplorerDataContract; namespace BruceZhang.WCF.DocumentsExplorerServiceImplementation { [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class DocumentsExplorerService : IDocumentsExplorerService { //Service Implementation } } |
Svc文件通过@ServiceHost指示符指定它所要托管的服务,此外还指定了实现服务的语言、调用模式,还可以设置CodeBehind,指定服务代码。不过,在IIS托管中,服务代码或程序集文件受到一定的限制,它只能放在如下的其中一个位置中:
(1) svc文件的内嵌代码中;
(2) 放在注册于GAC的单独程序集中;
(3) 驻留于应用程序的Bin文件夹内的程序集中(此时,bin文件夹不必include)
(4) 驻留于应用程序的App_Code文件夹的源代码文件中(根据Language的设置,或者为C#或者为VB);
即使我们将服务代码放在应用程序根目录下,或者其它文件夹中,然后通过CodeBehind指定代码的路径,仍然不能托管服务。
如果服务契约与服务类是通过引用的方式在宿主应用程序中,则我们可以直接创建一个扩展名为.svc的单个文件,然后include到应用程序根目录下,如图六中的HostService.svc,该文件没有关联的cs文件。此时,在Visual Studio中直接打开该文件,并不能编写服务代码,而是指定@ServiceHost即可。
注意,上述方式的IIS宿主只能创建ServiceHost实例,如果是自定义的ServiceHost,则需要通过@ServiceHost的Factory来指定创建自定义ServiceHost的工厂类。例如这样的自定义ServiceHost以及相应的工厂类:
| 以下是引用片段: using System; using System.ServiceModel; using System.ServiceModel.Activation; namespace BruceZhang.WCF.DocumentsExplorerIISHost { public class CustomServiceHostFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses) { CustomServiceHost customServiceHost = new CustomServiceHost(serviceType, baseAddresses); return customServiceHost; } } public class CustomServiceHost : ServiceHost { public CustomServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { } protected override void ApplyConfiguration() { base.ApplyConfiguration(); } } } |
| 以下是引用片段: <%@ ServiceHost Language="C#" Debug="true" Service="BruceZhang.WCF.DocumentsExplorerServiceImplementation.DocumentsExplorerService" Factory="BruceZhang.WCF.DocumentsExplorerIISHost.CustomServiceHostFactory"%> |
在IIS托管应用程序中,我们需要创建web.config(注意,不是app.config),在
| 以下是引用片段: <system.serviceModel>节中配置服务的相关内容: <system.serviceModel> <services> <service behaviorConfiguration="DocumentExplorerServiceBehavior" name="BruceZhang.WCF.DocumentsExplorerServiceImplementation.DocumentsExplorerService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="DocumentExplorerServiceBinding" contract="BruceZhang.WCF.DocumentsExplorerServiceContract.IDocumentsExplorerService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <bindings> <basicHttpBinding> <binding name="DocumentExplorerServiceBinding" sendTimeout="00:10:00" transferMode="Streamed" messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="9223372036854775807"> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="DocumentExplorerServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> |
| 以下是引用片段: 图七 在IIS站点中为IIS创建虚拟目录 |
如果站点的属性没有做任何修改,使用默认的端口号,以及Localhost,则访问服务的基地址为http://localhost/DocumentsExplorer/HostService.svc。如果在配置文件的服务endpoint中设置地址为DocumentsService,如:
通过IIS启动站点后,不需要做任何操作,服务宿主自动会创建ServiceHost实例或者Factory指定的自定义ServiceHost实例。 由于服务地址发生了变化,因此客户端的配置文件也需要做出相应的修改,必须将服务的地址设置为与之对应的地址。其中,服务的基地址为svc文件在IIS中的地址。 IIS宿主是一种主要的服务托管方式,这是因为它具有易用性、可维护性、安全性、易于部署等多个优势。然而,它却具有一个致命的阿客流斯之踵,那就是它只支持HTTP协议的传输绑定。特别对于局域网场景下,如果使用IIS宿主,就无法利用TCP传输的高效率,甚至无法使用MSMQ以及Peer to Peer传输。 IIS 7.0(基于Windows Vista和Windows Server 2007)提供的Windows激活服务(WAS)突破了IIS 6.0对于HTTP的依赖。 4、WAS宿主 WAS是IIS 7.0的一部分,但也可以独立地安装与配置。WAS支持所有可用的WCF传输协议、端口与队列。 利用WAS托管服务与IIS宿主托管服务的方法并没有太大的区别,仍然需要创建svc文件,同时在IIS中需要在站点中创建应有程序指向托管应用程序,还可以设置访问服务的别名与应用程序池。 由于WAS诉诸支持所有的绑定,因此此时的服务绑定并不会受到宿主的限制。
则公开服务的地址则为http://localhost/DocumentsExplorer/HostService.svc/DocumentsServic。
以下是引用片段:
<endpoint
address="DocumentsService"
binding="basicHttpBinding"
bindingConfiguration="DocumentExplorerServiceBinding"
contract="BruceZhang.WCF.DocumentsExplorerServiceContract.IDocumentsExplorerService" />
关注此文的读者还看过: