Extend ServiceHost on IIS Hosting Scenarios.
The motivation for API’s that I’m going to talk about in this post stems from the fact that the standard API for hosting services in WCF (System.ServiceModel.ServiceHost) is a first-class extensibility point in the WCF architecture. We expect that lots of folks will derive their own host classes from ServiceHost, usually with the intent of overriding ServiceHost.OnOpening in order to do interesting things with the ServiceDescription (e.g. adding default endpoints imperatively, or twiddling knobs on Behaviors) prior to opening the service.
In the self-host world, you can get away without creating a custom ServiceHost because you write the code that instantiates the host and subsequently calls Open on it. In between those two steps you can do whatever you want, e.g.:
public static void Main()
{
ServiceHost host = new ServiceHost( typeof( MyService ) );
host.Description.Behaviors.Add( new MyServiceBehavior() );
host.Open();
...
}
However, this solution has the downside of not being particularly reusable. The code that manipulates the description is baked into the host program (in this case, the Main() function) so if you want to reuse that logic in other contexts you’ve got a little bit of a problem. A slight refactoring will solve that problem; all we have to do is move the code that adds the ServiceBehavior out of Main and into the OnOpening method of a custom derivative of ServiceHost:
public class DerivedHost : ServiceHost
{
public DerivedHost( Type t, params Uri baseAddresses ) :
base( t, baseAddresses ) {}
public override void OnOpening()
{
this.Description.Behaviors.Add( new MyServiceBehavior() );
}
}
Then, inside of Main() you just say
public static void Main()
{
ServiceHost host = new DerivedHost( typeof( MyService ) );
host.Open();
...
}
And you’ve now encapsulated that custom logic up into a nice, clean abstraction that can be easily reused across lots of different host executables.
What’s not immediately obvious at this point is how to use this custom ServiceHost inside of IIS or WAS. Those environments are different than the self-host world, because the hosting environment is the one instantiating the ServiceHost on behalf of the application .The WAS hosting infrastructure doesn’t know anything about your custom ServiceHost derivative, and it seems like there’s no good place to write that new DerivedHost( typeof( MyService ) ); line when running in IIS/WAS. What’s a poor customer of the WCF extensibility model to do in this case?
This is exactly the problem that the ServiceHostFactory API was designed to solve.
Because the ServiceHost used to host the service is a potentially polymorphic type, the hosting environment never instantiates it directly. Instead, we use a factory pattern to provide a layer of indirection between the hosting environment and the concrete type of the service. Unless you tell us otherwise, we’ll use a default implementation (System.ServiceModel.Activation.ServiceHostFactory) that simply returns an instance of System.ServiceModel.ServiceHost. You can provide your own factory (one that presumably returns your derived host) by specifying the CLR type name of your factory implementation in the @ServiceHost directive.
The intent is that for simple cases, implementing your own factory should be a trivial exercise. For example, here’s a ServiceHostFactory that returns the DerivedHost from previous examples in this post:
public class DerivedFactory : ServiceHostFactory
{
public override ServiceHost CreateServiceHost(Type t,
Uri[] baseAddresses )
{
return new DerivedHost( t, baseAddresses )
}
}
To use this factory instead of the default factory, just provide the type name in the @ServiceHost directive as follows:
<% @ServiceHost Factory=”DerivedFactory” Service=”MyService” %>
While there’s not technical prohibition on doing pretty much whatever you want to the ServiceHost you return from ServiceHostFactory.CreateServiceHost(), our guidance here is to keep your factory implementations as light as possible. If you have lots of custom logic, you’re better off putting that inside your host instead of inside the factory for the same reusability reasons that you don’t want to put a lot of host-related code inside of Main().
manu