The WCF service class cannot exist in a void. Every WCF service must be hosted in a Windows process called the host process. A single host process can host multiple services, and the same service type can be hosted in multiple host processes. WCF has no restrictions regarding whether or not the host process is also the client process, although having a separate process promotes fault and security isolation. It is also immaterial who provides the process and what kind of process is involved. The host can be provided by Internet Information Services (IIS), by the Windows Activation Service (WAS) on Windows Vista or Windows Server 2008, Windows 7 or later, on the Windows Server AppFabric, or by the developer as part of the application.
Note
In-process (or in-proc) hosting, where the service resides in the same process as the client, is a special case. By definition, the developer provides the host for the in-proc case.
The main advantage of hosting a service on the Microsoft IIS web server is that the host process is launched automatically upon the first client request, and IIS 5/6 manages the lifecycle of the host process. The main disadvantage of IIS 5/6 hosting is that you can only use HTTP. With IIS 5, you are further restricted to having all services use the same port number.
Hosting in IIS is very similar to hosting a classic ASMX web service. You need to create a virtual directory under IIS and supply an .svc file. The .svc file functions similarly to an .asmx file and is used to identify the service code behind the file and class. Example 1-2 shows the syntax for the .svc file.
Example 1-2. A .svc file
<%@ ServiceHost Language = "C#" Debug = "true" CodeBehind = "˜/App_Code/MyService.cs" Service = "MyService" %>
Note
You can even inject the service code inline in the .svc file, but that is not advisable, as is the case with ASMX web services.
When you use IIS 5/6 hosting, the base address used for the service always has to be the same as the address of the .svc file.
You can use Visual Studio 2010 to generate a boilerplate IIS-hosted service. From the File menu, select New Web Site, then select WCF Service from the New Web Site dialog box. Visual Studio 2010 creates a new website, a service code, and a matching .svc file. You can also use the Add New Item dialog box to add another service later.
The website config file (web.config) typically lists the types you want to expose as services. You need to use fully qualified type names, including the assembly name if the service type comes from an unreferenced assembly:
<system.serviceModel> <services> <service name = "MyNamespace.MyService"> ... </service> </services> </system.serviceModel>
Instead of defining an .svc file, you can provide the service
type and its address information directly in the application
web.config file in the serviceHostingEnvironment
section. In fact, you can list there
as many services as you like:
<system.serviceModel> <serviceHostingEnvironment> <serviceActivations> <add relativeAddress = "MyService.svc" service = "MyNamespace.MyService"/> <add relativeAddress = "MyOtherService.svc" service = "MyOtherService"/> </serviceActivations> </serviceHostingEnvironment> <services> <service name = "MyNamespace.MyService"> ... </service> <service name = "MyOtherService"> ... </service> </services> </system.serviceModel>
Self-hosting is the technique in which the developer is responsible for providing and managing the lifecycle of the host process. Use self-hosting when you want a process (or machine) boundary between the client and the service and when you are using the service in-proc—that is, in the same process as the client. You can provide any Windows process, such as a Windows Forms application, a WPF application, a Console application, or a Windows NT Service. Note that the process must be running before the client calls the service, which typically means you have to prelaunch it. This is not an issue for NT Services or in-proc hosting. You can provide a host with only a few lines of code. Unlike IIS 5/6, a self-hosted service can use any WCF transport protocol, and you can take advantage of all the WCF features, including the service bus, discovery, and utilize a singleton service.
As with IIS 5/6 hosting, the hosting application config file (app.config) typically lists the types of the services you wish to host and expose to the world:
<system.serviceModel> <services> <service name = "MyNamespace.MyService"> ... </service> </services> </system.serviceModel>
In addition, the host process must explicitly register the
service types at runtime and open the host for client calls, which is
why the host process must be running before the client calls arrive.
Creating the host is typically done in the Main()
method using the class ServiceHost
, defined in Example 1-3.
Example 1-3. The ServiceHost class
public interface ICommunicationObject
{
void Open();
void Close();
//More members
}
public abstract class CommunicationObject : ICommunicationObject
{...}
public abstract class ServiceHostBase : CommunicationObject,IDisposable,...
{...}
public class ServiceHost
: ServiceHostBase
{
public ServiceHost(Type serviceType,params Uri[] baseAddresses);
//More members
}
You need to provide the constructor of ServiceHost
with the service type and
optionally with default base addresses. The set of base addresses can
be an empty set, and even if you provide base addresses, you can
configure the service to use different base addresses. Having a set of
base addresses enables the service to accept calls on multiple
addresses and protocols and to use only a relative URI.
Note that each ServiceHost
instance is associated with a particular service type, and if the host
process needs to host multiple types of services, you will need a
matching number of ServiceHost
instances. By calling the Open()
method on the
host, you allow calls in, and by calling the Close()
method, you
gracefully exit the host instance, allowing calls in progress to
complete while refusing future client calls even if the host process
is still running. Closing the service host is typically done when the
host process shuts down. For example, to host this service in a
Windows Forms application:
[ServiceContract] interface IMyContract {...} class MyService : IMyContract {...}
you would write the following hosting code:
static void Main() { ServiceHost host = new ServiceHost(typeof(MyService)); host.Open()
; //Can do blocking calls: Application.Run(new MyForm()); host.Close()
; }
Opening a host loads the WCF runtime and launches worker threads to monitor incoming requests. The monitoring threads dispatch incoming calls to worker threads from the I/O completion thread pool (where there are up to 1,000 threads by default). Since worker threads are involved, you can perform blocking operations after opening the host.
Because the host is closed gracefully, the amount of time it
will take is undetermined. By default, the host will block for 10
seconds waiting for Close()
to
return and will proceed with the shutdown after that timeout has
expired. Before opening the host, you can configure a different close
timeout with the CloseTimeout
property
of ServiceHostBase
:
public abstract class ServiceHostBase : ... { public TimeSpan CloseTimeout {get;set;} //More members }
For example, you can use programmatic calls to set the close timeout to 20 seconds:
ServiceHost host = new ServiceHost(...); host.CloseTimeout = TimeSpan.FromSeconds(20); host.Open();
You can do the same in a config file by placing the close
timeout in the host
section of the
service:
<system.serviceModel> <services> <service name = "MyNamespace.MyService"> <host> <timeouts closeTimeout = "00:00:20" /> </host> ... </service> </services> </system.serviceModel>
Visual Studio 2010 allows you to add a WCF service to any application project by selecting WCF Service from the Add New Item dialog box. A service added this way is, of course, in-proc toward the host process, but out-of-proc clients can also access it.
You can launch a service host without providing any base address by omitting the base addresses altogether:
ServiceHost host = new ServiceHost(typeof(MyService));
Warning
Do not provide a null
instead of an empty list, because that will throw an
exception:
ServiceHost host; host = new ServiceHost(typeof(MyService),null);
You can also register multiple base addresses separated by
commas, as in the following snippet, as long as the addresses do not
use the same transport scheme (note the use of the params
qualifier in Example 1-3):
Uri tcpBaseAddress = new Uri("net.tcp://localhost:8001/");
Uri httpBaseAddress = new Uri("http://localhost:8002/");
ServiceHost host = new ServiceHost(typeof(MyService),
tcpBaseAddress,httpBaseAddress
);
WCF also lets you list the base addresses in the host config file:
<system.serviceModel> <services> <service name = "MyNamespace.MyService"> <host> <baseAddresses> <add baseAddress = "net.tcp://localhost:8001/"/> <add baseAddress = "http://localhost:8002/"/> </baseAddresses> </host> ... </service> </services> </system.serviceModel>
When you create the host, it will use whichever base addresses it finds in the config file, plus any base addresses you provide programmatically. Take extra care to ensure the configured base addresses and the programmatic ones do not overlap in the scheme.
Note
On Windows Vista, Windows Server 2008, and Windows 7 (or
later), for HTTP addresses other than port 80, you will need to
launch the host process (or Visual Studio 2010 while testing or
debugging) as an administrator. Instead of doing that every time,
you can instruct Windows to reserve the port namespace for the user running the
host. Do this using the netsh.exe
command-line utility. For
example, to reserve the HTTP port 8002 on the local machine, you
will need to run this command at a command prompt launched as an
administrator:
netsh http add urlacl url=http://+:8002/ user="MachineOrDomain\UserName"
You can even register multiple hosts for the same type, as long as the hosts use different base addresses:
Uri baseAddress1 = new Uri("net.tcp://localhost:8001/"); ServiceHost host1 = new ServiceHost(typeof(MyService),baseAddress1); host1.Open(); Uri baseAddress2 = new Uri("net.tcp://localhost:8002/"); ServiceHost host2 = new ServiceHost(typeof(MyService),baseAddress2); host2.Open();
However, with the exception of some threading issues discussed
in Chapter 8, opening multiple hosts
this way offers no real advantage. In addition, opening multiple
hosts for the same type does not work with base addresses supplied
in the config file and requires use of the ServiceHost
constructor.
The ICommunicationObject
interface that
ServiceHost
supports offers some
advanced features, listed in Example 1-4.
Example 1-4. The ICommunicationObject interface
public interface ICommunicationObject { void Open(); void Close(); void Abort(); event EventHandler Closed; event EventHandler Closing; event EventHandler Faulted; event EventHandler Opened; event EventHandler Opening; IAsyncResult BeginClose(AsyncCallback callback,object state); IAsyncResult BeginOpen(AsyncCallback callback,object state); void EndClose(IAsyncResult result); void EndOpen(IAsyncResult result); CommunicationState State {get;} //More members } public enum CommunicationState { Created, Opening, Opened, Closing, Closed, Faulted }
If opening or closing the host is a lengthy operation, you can
do so asynchronously with the BeginOpen()
and
BeginClose()
methods. You can subscribe to hosting events such as state changes
or faults, and you can use the State
property to
query for the host status. Finally, the ServiceHost
class
also offers the Abort()
method.
Abort()
is an ungraceful
exit—when called, it immediately aborts all service calls in
progress and shuts down the host. Active clients will each get an
exception.
You can improve on the WCF-provided ServiceHost
class by defining the ServiceHost<T>
class, as shown in
Example 1-5.
Example 1-5. The ServiceHost<T> class
public class ServiceHost<T> : ServiceHost { public ServiceHost() : base(typeof(T)) {} public ServiceHost(params string[] baseAddresses) : base(typeof(T), baseAddresses.Select(address=>new Uri(address)).ToArray()) {} public ServiceHost(params Uri[] baseAddresses) : base(typeof(T),baseAddresses) {} }
ServiceHost<T>
provides simple constructors that do not require the service type as
a construction parameter and that can operate on raw strings instead
of the cumbersome Uri
. I’ll add
quite a few extensions, features, and capabilities to ServiceHost<T>
throughout this
book.
The problem with hosting in IIS 5/6 is that it is a web server, not a hosting engine. It therefore requires you to masquerade your service as a website. While ASP.NET encapsulates this step for you, it causes a significant increase in internal complexity, involving the HTTP modules and the ASP.NET pipeline. The problem is that the more moving parts involved, the higher the likelihood of something going wrong. As a result, hosting in IIS 5/6 is notorious for instability and the frequent need to reset the server or IIS 5/6. Moreover, limiting the service to using only HTTP makes IIS 5/6 ill-suited for intranet applications.
With the next wave of Windows, Microsoft rectified this issue by providing a general-purpose hosting engine called the Windows Activation Service (WAS). WAS is a system service available with Windows Vista, Windows Server 2008, and Windows 7 (or later). The WAS is a true general-purpose hosting engine. It can host websites (in fact, IIS 7 will host its websites in the WAS by default), but it can just as easily host your services, allowing you to use any transport, such as TCP, IPC, or MSMQ. You can install and configure the WAS separately from IIS 7. Hosting a WCF service in the WAS is designed to look just like hosting in IIS 5/6. You need to either supply an .svc file, just as with IIS 5/6, or provide the equivalent information in the config file. All the other development aspects, such as support in Visual Studio 2010, remain exactly the same. Since the WAS is a system service, you do not need to pre-launch your service host process. When the first client call arrives, the WAS will intercept it, launch a worker process to host your service, and forward it the call.
WAS offers many advantages over self-hosting, including application pooling, recycling, idle time management, identity management, and isolation, and it is the host process of choice when available—that is, when you can target a platform that supports it, such as Windows Server 2008 (or later) machine for scalability, or a Windows Vista or Windows 7 (or later) client machine for a handful of clients.
That said, self-hosted processes do offer singular advantages, such as in-proc hosting, dealing well with unknown customer environments, and easy programmatic access to the advanced hosting features described previously.
It is often the case that you need to interact with the
host instance. While this is integral to the use of a self-hosting
solution, when using IIS 5/6 or WAS, you have no direct access to the host. To overcome
this hurdle, WCF provides a hook called a host factory. Using the Factory
tag in the
.svc file, you can specify a
class you provide that creates the host instance:
<%@ ServiceHost Language = "C#" Debug = "true" CodeBehind = "˜/App_Code/MyService.cs" Service = "MyService" Factory = "MyServiceFactory" %>
You can also specify the host factory in the config file when not using an .svc file explicitly:
<serviceActivations>
<add relativeAddress = "MyService.svc"
service = "MyService"
factory = "MyServiceFactory"
/>
</serviceActivations>
The host factory class must derive from the ServiceHostFactory
class and override the
CreateServiceHost()
virtual
method:
public class ServiceHostFactory : ... { protected virtual ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses); //More members }
For example:
class MyServiceFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { ServiceHost host = new ServiceHost(serviceType,baseAddresses); //Custom steps here return host; } }
The problem with the WAS is that it is a general-purpose hosting engine. It is actually unaware that it is hosting a WCF service or a website. It is not optimized for hosting services. To address this issue, you can install an extension to the WAS called the Windows Server AppFabric. Windows Server AppFabric requires IIS 7.5, that is, a machine running Windows 7, Windows Server 2008 R2, and .NET 4.0. The extension is designed to provide additional configuration options as well as monitoring, instrumentation, and event tracking for both WCF services and Workflow (WF) services. In fact, Windows Server AppFabric is geared more toward WF services, which require support for persistence and state management correlation. Windows Server AppFabric adds items for managing and monitoring the services, as well as WCF and WF configuration items, to the IIS 7 management console. Windows Server AppFabric provides a dashboard for monitoring the running instances of WCF or WF services, and is reminiscent of the MTS or COM+ Component Services Explorer of old. Windows Server AppFabric provides health monitoring and custom diagnostics, as well as some troubleshooting features for analyzing why a service call has failed.
Windows Server AppFabric also supports scripting of all the options available in the user interface. Windows Server AppFabric offers its own events collecting system service, which stores the events in a SQL Server database. You can provide Windows Server AppFabric with tracing and tracking profiles at various verbosity levels. A full discussion of the features of Windows Server AppFabric is beyond the scope of this chapter, as it pertains more to administration and operations, and less to service design and software development, which is the focus of this book.
By far, the most pertinent feature of Windows Server AppFabric for WCF developers is the capability to auto-start services without waiting for the first client request. Without the Windows Server AppFabric, the WAS will launch your hosting process only when the first client request comes in. It turns out there are a number of cases in which this will not suffice, for example, when by design the service must be running before the first client calls. These cases include a singleton service (discussed in Chapter 4), a service that uses the service bus (discussed in Chapter 11), or a discovery-enabled service (discussed in Appendix C).
When you require the service to start up independently of client calls, you can instruct Windows Server AppFabric to do just that. Right-click the service site in the II7 console, select Manage WCF and WF Services, and click Configure… to bring up the dialog box shown in Figure 1-4.
Select the Auto-Start tab and specify whether you want to start up all services in the application or only services that opt-in to start up (the Custom option). If you choose to opt-in individual services, you will then need to enable auto-start for each service individually (by configuring its Auto-Start option on the properties from the Services dashboard). This will include a directive for that service in the web.config file:
<microsoft.applicationServer> <hosting> <serviceAutoStart> <add relativeVirtualPath = "MyService.svc"/> </serviceAutoStart> </hosting> </microsoft.applicationServer>
Even though WCF offers such a variety of options, from IIS 5/6, to the WAS, to the WAS with AppFabric, to self hosting, it is easy to choose the correct host. For an Internet application (that is, an application that receives calls from clients across the Internet), follow the decision tree shown in Figure 1-5.
If your Internet application can use the service bus and is hosted on a machine running IIS 7.5 (.NET 4.0, Windows Server 2008 R2, or Windows 7 or later), then you should host in Windows Server AppFabric. Otherwise, you must use self-hosting to manually auto-start your service. If your Internet application cannot use the service bus but can target a machine running IIS 7 (Windows Vista, Windows Server 2008, or Windows 7 or later), you should choose the WAS. Otherwise, choose IIS 6.
For an intranet application (that is, an application that receives calls from clients within the same intranet), follow the decision tree shown in Figure 1-6.
If your intranet application is deployed on an end user client machine and uses an interactive user interface, or has its own deployment lifecycle and updates, use self-hosting. If the application is deployed on a server machine running IIS 7.5, you should host in Windows Server AppFabric. If you cannot use AppFabric and your service is a singleton or uses WCF discovery, then you must choose self-hosting to manually autostart your service. If you cannot use AppFabric and your service is not a singleton and does not rely on discovery, the next question is about what kind of platform it is running on. If the service machine is running IIS 7, you should choose the WAS. Otherwise, choose self-hosting.
Get Programming WCF Services, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.