One of the most common exceptions people seem to run into when building IIS/WAS-hosted WCF services is the dreaded
Parse Error: The unknown directive @Service was specified.
This is caused by a syntactic change we made during the latter part of the development cycle (I think it went in between WCF Beta2 and WCF RC0, but my memory is hazy). The new directive is names @ServiceHost, and it usually looks like this:
<% @ServiceHost Service=”MyService,MyNamespace”>
That’s the simplest possible form of the @ServiceHost directive. There are other attributes you can specify (such as Language and CodeBehind) which govern how any code you might provide in-line in the .svc file gets compiled. However, the only required attribute is Service, which contains the CLR type name of your service implementation type. During service activation, we’ll load up an instance of the type you specify in the Service attribute and derive the service’s Name from the [ServiceBehavior] attribute if there is one. Then we use the service’s Name as a key into configuration in order to look up any endpoints or behaviors that might be associated with that service in config. See my post A Brief Tour of Service Activation for more details.
One of the reasons we made this change was somewhat philosophical. I’ve always looked for ways to draw analogies between the IIS-hosted world and the self-hosted world. In self-host, the way to activate a service is to say new ServiceHost() and pass in your implementation type. The hosted equivalent of this is an .svc file with the implementation type passed in as a string through the directive at the top of the page. Thus, the .svc file is really more of a metaphor for the thing hosting the service rather than the service itself (which is an independent entity that’s not affiliated with a particular host). It’s a subtle point, but that always bugged me about the old design. Had that been the only thing, we probably would have just treated it as a naming nit and moved on with life. However, there’s a second reason that ultimately pushed us toward taking the DCR and changing the syntax.
This reason has to do with the fact that the ServiceHost class is a legitimate extensibility point in our model. It’s very common to derive your own implementation of ServiceHost and use method overrides to add new functionality. For example, you can override OnOpening() to futz with the Description prior to service activation.
In self-host, it’s very easy to use your fancy derived implementation of ServiceHost. You simply say new DerivedServiceHost() instead of new ServiceHost() and you’re off to the races. Unfortunately, since the hosting environment is the one responsible for actually instantiating the ServiceHost in this scenario you need some way to tell the hosing environment to use your DerivedServiceHost instead of the default.
In the olden days, we supported this scenario by overloading the Class attribute of the @Service directive. You could use this attribute to carry either the CLR type name of your service implementation or the CLR type name of your custom host. E.G. you could do the following and it would Just Work:
<% @Service Class="DerivedServiceHost, MyNamespace" %>
Unfortunately, this design meant that you couldn't easily reuse your custom host across multiple service types. There was a workaround involving the creation of lots of turd classes:
public class FooServiceHost : DerivedServiceHost
{
public FooServiceHost( Uri[] baseAddresses ) : base( typeof( FooService ), baseAddresses ) {}
}
However, that clearly sucked.
We came to the ultimate realization that you wanted to be able to provide both the host type as well as the service type. As such, we arrived at a model whereby the file represented the ServiceHost instead of the Service. If you didn't specify a type for the host, we defaulted to System.ServiceModel.ServiceHost. Hosts aren't much good unless they're associated with a service, which is why the Service attribute is required in all cases.
The important syntax for the @ServiceHost directive is here:
<% @ServiceHost Factory="MyFactory, MyNamespace" Service="MyService, MyOtherNamespace" %>
This is getting somewhat long, so I'll come back to why we chose a Factory pattern for the creation of ServiceHost's and some nifty things you can do through creating your own ServiceHostFactory in another post.