WCF services can listen for messages anywhere: Windows Services, desktop applications, and Internet Information Services (IIS). All environments use the same configuration elements. In all cases, an instance of a ServiceHost will be used to reflect over a service implementation to determine service requirements. The ServiceHost then marries the code with any configuration to produce an entity that can listen for, dispatch, and respond to messages that arrive over the various transports.
To demonstrate hosting, we use the following contract:
namespace DZone.Contracts
{
[System.ServiceModel.ServiceContract(
Namespace = “http://www.dzone.com/WCF”)]
public interface IMyService
{
[System.ServiceModel.OperationContract]
string SayHi(string name);
}
}
Implemented by the following class:
namespace DZone.Services
{
public class MyService : DZone.Contracts.IMyService
{
public string SayHi(string name)
{
return string.Format(“Console: Hello, {0}”, name);
}
}
}
and use the following configuration:
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name=”MyServiceBehavior”>
<serviceMetadata httpGetEnabled=”true”/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name=”DZone.Services.MyService”
behaviorConfiguration=”MyServiceBehavior”>
<host>
<baseAddresses>
<add baseAddress=”http://localhost/Demo”/>
</baseAddresses>
</host>
<endpoint contract=”DZone.Contracts.IMyService”
binding=”basicHttpBinding” address=”/MyService”/>
<endpoint kind=”mexEndpoint” address=”/mex”
binding=”mexHttpBinding” />
</service>
</services>
</system.serviceModel>
</configuration>
The configuration declares that there is a serviceBehavior named MyServiceBehavior which supports metadata exchange. This behavior is attached to a service instance whose name is DZone.Services.MyService and matches the name of the service implementation. The host has a base address of http://localhost/Demo. This base address is used for any bindings that support the http scheme. The service exposes two endpoints. One endpoint exposes an implementation of the DZone.Contracts.IMyService contract listening off the http base address at /MyService. The other endpoint hosts metadata exchange using a predefined binding named mexHttpBinding listening off the http base address at /Mex.
Hosting in a Console/GUI application
Using this configuration, we can host the service in a Console application with the following code:
var host = new ServiceHost(typeof(MyService))
host.Open();
Console.WriteLine(“Press [Enter] to exit”);
Console.ReadLine();
try
{
((IDisposable)host).Dispose();
}
catch (CommunicationObjectFaultedException ex)
{
// TODO: add code to log
}
Once the using block exits, host.Dispose() is called. You can do something similar in a WinForm/WPF application by opening the service on window Load and explicitly calling ServiceHost.Dispose() when the window is closed/unloaded.
Hosting in a Windows Service
A Windows Service requires you to be able to respond to Start and Resume events very quickly. In order to do this, you do not want to block in the start if at all possible. Creating a single WCF ServiceHost does not normally take much time. However, we should always be prepared for things to take a while and have the services behave nicely. To host the same WCF service in a Windows Service, write the following code:
private ServiceHost _host;
protected override void OnStart(string[] args)
{
ThreadPool.QueueUserWorkItem(StartListening, this);
}
static void StartListening(object state)
{
var service = state as DemoService;
if (service != null)
{
service._host = new ServiceHost(typeof(MyService));
service._host.Faulted +=
(s, e) =>
{
service.StopListening();
service._host = null;
StartListening(service);
};
}
}
protected override void OnStop()
{
StopListening();
}
void StopListening()
{
try
{
((IDisposable)_host).Dispose();
}
catch (CommunicationObjectFaultedException ex)
{
// TODO: add code to log
}
}
The preceding code executes the initialization logic on a separate thread via ThreadPool.QueueUserWorkItem. The ServiceHost will run for a long time. If the ServiceHost gets into a situation where it can no longer listen for messages by entering the Faulted state, the code should start up a new instance of the ServiceHost. When the service is done listening, a CommunicationObjectFaultedException may be thrown on Dispose. Because this exception is expected, the code will log the exception. When the service stops, we call StopListening. If you want to support Pause and Resume, add the following:
protected override void OnPause()
{
StopListening();
base.OnPause();
}
protected override void OnContinue()
{
ThreadPool.QueueUserWorkItem(StartListening, this);
base.OnContinue();
}
Hosting in IIS
To host a service in IIS, you create a .svc file which is hosted at a path of your choosing in your web application. That will give you a file that looks like this:
<%@ ServiceHost Service=”DZone.Services.MyService” %>
The config remains largely the same. IIS will automatically set the http base address for the host to be the address of the .svc file.
In IIS 7 and later, you can also host services with NetTcpBinding and NetNamedPipeBinding. To do this, run the following commands:
TCP Activation:
- Run the following command (on one line):
%windir%\system32\inetsrv\appcmd.exe set site “Default Web Site” -+bindings.
[protocol=’net.tcp’,bindingInformation=’808:*’]
- Run the following command to enable the http and net. pipe protocol on your site (on one line):
%windir%\system32\inetsrv\appcmd.exe set app
“Default Web Site/[your v-dir]” /enabledProtocols:http,net.tcp
Named Pipe Activation
- Run the following command (on one line):
%windir%\system32\inetsrv\appcmd.exe set site “Default Web Site”
-+bindings.[protocol=’net.pipe’,bindingInformation=’*’]
- Run the following command to enable the http and net. pipe protocol on your site (on one line):
%windir%\system32\inetsrv\appcmd.exe set app
“Default Web Site/[your v-dir]” /enabledProtocols:http,net.pipe
With Vista SP1 and Server 2008, you can also enable these protocols in the IIS Manager.
For the demo application, remove the <host> base addresses and add in these two endpoints:
<endpoint contract=”DZone.Contracts.IMyService” address=”/MyService”
binding=”netTcpBinding” />
<endpoint contract=”DZone.Contracts.IMyService” address=”/MyService”
binding=”netNamedPipeBinding” />
OperationContext
Every incoming message is associated with an OperationContext. Think of OperationContext as WCF’s version of HttpContext. OperationContext.Current yields the current operation context with pointers to the following commonly used properties:
IncomingMessageHeaders |
Headers on the incoming message. |
OutgoingMessageHeaders |
Can use this to add more headers to the response. |
IncomingMessageProperties |
The message properties serve as a mechanism to send information in between layers within the message processing pipeline about a specific message. |
ServiceSecurityContext |
Gain access to the identity of the currently logged in user. Also available through ServiceSecurityContext.Current. Contains a property, AuthorizationContext, where you can investigate the ClaimSets for the current user. |
The OperationContext also has two often used methods:
SetTransactionComplete() |
Allows the service to complete a transaction in code instead of automatically on exit. |
GetCallbackChannel<T>() |
For duplex services, allows the service to send messages back to the service. |
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}