WCF Extensibility – ServiceHostFactory
This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page .
Ever since its first version, WCF was designed with a very rich configuration feature, where you could define the server (and client) endpoints, bindings, behaviors, quotas, throttling, and many other features of the service without touching a single line of code – no need for recompiling, even after the service was deployed. It’s a great idea – the developer worries about the logic of the service (including the contracts and the implementation), without needing to worry about how the service code was going to be used. The people responsible for deploying the service, on the other hand, would be more knowledgeable about which addresses to use, which bindings to use (e.g., HTTP for external-facing services with possible some firewall configurations, TCP for services inside a corporate network), which certificates to use for securing the service, etc. The “IT pro” didn’t need to have a deep understanding of the code, and could focus on a simple configuration / XML interface for defining the services, including not only the things I mentioned such as addresses and bindings, but also more fine-tuned settings such as throttling and security quotas and local behaviors. This created a (somewhat) clear division of tasks between the “developer” and the “IT Pro” roles. In some cases where the code actually depended on some deployment settings, a validation error is thrown right when the service is being opened, so it’s easy to spot the discrepancy and fix them in a simple test – this is the case, for example, with dependency on some ASP.NET classes (and the ASP.NET compatibility requirements), or whether a service requires or not a session-full channel (which restricts which bindings can be used).
This is a good paradigm in theory. There are, however, some dependencies between the code and the configuration which don’t have any clear indication, and the service will appear to open without errors, and only at runtime the problem will show up. For example, if the implementation of the service needs to look at the headers from the HTTP request (maybe only in some scenarios), it doesn’t make sense for the endpoints in the service to use non-HTTP bindings, and unless there’s a good interaction between the developer and the person responsible for deployment, it’s possible that this error won’t be caught until the service is in production. This issue (implicit dependencies between the code and the binding) can be fixed if the developer makes them explicit – for example, using some of the behaviors and using their Validate method to check the binding used at the time the host is being opened.
Another problem with the separation between “developer” and “IT pro” is that, in practice, often the line between the two roles isn’t clearly defined – in small companies the developer and the IT pro end up being the same person. So by splitting the service in two parts (implementation + deployment) we end up with two pieces of the project which need to be synchronized. This may sound like a small problem, but I’ve seen many, many issues in the forums where one tries to change some settings for the service, but the change isn’t applied. In most of the cases, what is happening is a (bad, unintended) side effect of a new feature in WCF on .NET Framework 4.0, the simplified configuration. Before, the user was forced to specify an endpoint in configuration (or in code, if programming against the ServiceHost directly), otherwise the service wouldn’t start. To make things simpler, the WCF team decided to add a default endpoint if none was specified, so the configuration file would be smaller. The problem happened then when the user had some thing like this service class:
- namespace WcfServiceLibrary1
- {
- public class Service1 : IService1
- {
- // ...
- }
- }
And tried to create a configuration as such:
- <system.serviceModel>
- <services>
- <service name="Service1">
- <endpoint address="" binding="wsHttpBinding" contract="IService1" />
- </service>
- </services>
- </system.serviceModel>
When the service started, the user expected it to have an endpoint with the WSHttpBinding, but it used the default one (BasicHttpBinding). The problem is that the “name” attribute must match the fully-qualified name of the service class (in this case, WcfServiceLibrary1.Service1). When going though configuration, the <service> element for the (non-existing) service “Service1” is simply ignored, and since there’s no intellisense (as of Visual Studio 2010) for service names in the config file, debugging this problem can be quite hard.
One simple solution for this issue is to simply not using configuration at all, by self-hosting the service (which is what I used in most of the code examples in this series, as it is more self-contained). But by not hosting the WCF service in IIS we lose a lot of the functionality of that platform, such as on-demand activation, application pool recycling, among others. On IIS-hosted services, we typically need a .svc file, with a one-line declaration of the service to be hosted using the @ServiceHost directive:
<%@ ServiceHost Language="C#" Debug="true" Service="WcfServiceLibrary1.Service1" %>
This format, however, doesn’t give the developer access to the service host to be able to configure the service in code. However, there is an optional attribute for the @ServiceHost directive which does exactly that – and after a long introduction we finally get to the point of this entry – the ServiceHostFactory. The factory is a class (unlike the previous extensibility points, which were interfaces), which you can extend and override some methods to control, via code (and all the goodness of intellisense and compile-time checks), all the properties of the service which is being hosted.
Public implementations in WCF
- WebServiceHostFactory: a factory which defines a WCF HTTP endpoint (useful in REST scenarios), by returning an instance of the WebServiceHost instead of the regular ServiceHost class. This simplifies the creation of WCF HTTP endpoints, as the WebServiceHost automatically sets the correct binding (WebHttpBinding) and behavior (WebHttpBehavior) for the contract interface defined in the service.
- WebScriptServiceHostFactory: similar to the previous one, this time it defines an endpoint to be used by ASP.NET AJAX pages, which includes features like a JavaScript proxy.
Class declaration
- public class ServiceHostFactory : ServiceHostFactoryBase
- {
- public ServiceHostFactory();
- public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses);
- protected virtual ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses);
- }
The ServiceHostFactory class has two virtual CreateServiceHost methods which the developer can override, but in the vast majority of the cases only the CreateServiceHost(Type, Uri[]) will be used (the other one takes the a string representing service, and in the ServiceHostFactory class it will use that to load the actual Type object used for the service class). The subclasses can then create the service host object, and at that point it has complete control over it, possibly bypassing configuration altogether. Another thing which I’ve seen often is that if a scenario is important enough that other people may want to use it even outside of IIS, then instead of simply providing a service host factory which can configure that scenario, we’ll also have a new service host class, and the CreateServiceHost method simply returns a new instance of that new service host. That’s the case with the WebServiceHostFactory (returning a WebServiceHost) and the WebScriptServiceHostFactory (which returns a WebScriptServiceHost, although that class is internal, since it cannot be used outside of IIS / ASP.NET).
How to use a custom service host factory
Via the @ServiceHost directive: by setting the “Factory” attribute to the fully-qualified name of the class derived from ServiceHostFactory
- <%@ ServiceHost Language="C#" Debug="true" Service="WebApplication1.Service1"
- Factory="WebApplication1.MyServiceHostFactory" %>
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Activation;
- namespace WebApplication1
- {
- public class MyServiceHostFactory : ServiceHostFactory
- {
- protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
- {
- ServiceHost host = new ServiceHost(serviceType, baseAddresses);
- host.AddServiceEndpoint(typeof(IService1), new WSHttpBinding(), "");
- return host;
- }
- }
- }
Via the ASP.NET Routes integration (new in 4.0) : when adding a service activation via the ServiceRoute class, we can (we actually have to) pass a ServiceHostFactory instance to the constructor.
- public class Global : System.Web.HttpApplication
- {
- protected void Application_Start(object sender, EventArgs e)
- {
- RouteTable.Routes.Add(new ServiceRoute("service", new MyServiceHostFactory(), typeof(Service1)));
- }
- }
Via the < serviceActivations > element in web.config: similar to the routes integration, with the advantage that it does not require ASP.NET compatibility enabled, but the relative address is limited to .svc (it essentially emulates a “normal” .svc file in the application directory).
- <system.serviceModel>
- <serviceHostingEnvironment>
- <serviceActivations>
- <add service="WebApplication1.Service1"
- relativeAddress="MyService.svc"
- factory="WebApplication1.MyServiceHostFactory "/>
- </serviceActivations>
- </serviceHostingEnvironment>
- </system.serviceModel>
Real world scenario: setting a DataContractResolver in hosted services
One of the new features for WCF in .NET 4.0 was the addition of the DataContractResolver for configuring known types dynamically (and possibly reducing the size of the messages). Youssef has a great description (with examples) of the feature at his blog, so I won’t spend much time talking about the virtues of the resolver. One of the issues we have with it, however, is that it cannot be defined in configuration (just like the IDataContractSurrogate to be used by the serializer). So we need either to define a service / endpoint / contract / operation behavior which will set that value, or we can create a very simple factory class which will set up the service and setting the appropriate resolver for us, with the added benefit that it will also not require anything in web.config.
And here goes the usual disclaimer – this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, the service host factory is quite a simple extensibility point – it only gives you access to the ServiceHost object in hosted scenarios, and it’s that object which will need to be manipulated (possibly using other extensibility points), so the service in this sample is quite simple (borrowed from Youssef’s post). For simplicity sake it doesn’t have a lot of error handling which a production-level code would.
So, partly borrowed from Youssef’s post, here are some polymorphic classes which we’ll transfer. Notice that none of them is decorated with [KnownType], and the service interface won’t be either.
- [DataContract]
- public abstract class Animal
- {
- [DataMember]
- public int Age { get; set; }
- [DataMember]
- public string Name { get; set; }
- public override string ToString()
- {
- return string.Format("Animal[Name={0}, Age={1}]", this.Name, this.Age);
- }
- }
- [DataContract]
- public class Dog : Animal
- {
- [DataMember]
- public DogBreed Breed { get; set; }
- public override string ToString()
- {
- return string.Format("Dog[Base={0}, Breed={1}]", base.ToString(), this.Breed);
- }
- }
- [DataContract]
- public class Cat : Animal
- {
- [DataMember]
- public CatBreed Breed { get; set; }
- public override string ToString()
- {
- return string.Format("Cat[Base={0}, Breed={1}]", base.ToString(), this.Breed);
- }
- }
- public enum DogBreed
- {
- GermanShepherd,
- GoldenRetriever,
- Poodle,
- }
- public enum CatBreed
- {
- Siamese,
- Persian,
- CornishRex,
- }
And now the service interface and implementation, which for this post I’ll leave as brief as it can be.
- [ServiceContract]
- public interface IPolymorphicService
- {
- [OperationContract]
- string PrintAnimal(Animal animal);
- }
- public class PolymorphicService : IPolymorphicService
- {
- public string PrintAnimal(Animal animal)
- {
- return animal.ToString();
- }
- }
As it’s defined, this service will not work, since the Animal class is abstract and there are no “known types” declared for it. Let’s use a resolver to solve this problem.
- public class AnimalResolver : DataContractResolver
- {
- const string MyAnimalsNamespace = "https://www.myAnimals.com";
- public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
- {
- if (typeNamespace == MyAnimalsNamespace)
- {
- if (typeName == typeof(Dog).Name)
- {
- return typeof(Dog);
- }
- else if (typeName == typeof(Cat).Name)
- {
- return typeof(Cat);
- }
- }
- return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
- }
- public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
- {
- if (type == typeof(Dog) || type == typeof(Cat))
- {
- XmlDictionary dictionary = new XmlDictionary();
- typeName = dictionary.Add(type.Name);
- typeNamespace = dictionary.Add(MyAnimalsNamespace);
- return true;
- }
- else
- {
- return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
- }
- }
- }
And finally a factory which will be used to plug this resolver into the WCF pipeline.
- public class PolymorphicServiceFactory : ServiceHostFactory
- {
- protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
- {
- ServiceHost host = new ServiceHost(serviceType, baseAddresses);
- ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IPolymorphicService), new BasicHttpBinding(), "");
- foreach (var operation in endpoint.Contract.Operations)
- {
- DataContractSerializerOperationBehavior dcsob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
- if (dcsob != null)
- {
- dcsob.DataContractResolver = new AnimalResolver();
- }
- }
- return host;
- }
- }
Now we can define the .svc file,
- <%@ ServiceHost Language="C#" Debug="true" Service="Service.PolymorphicService"
- Factory="Service.PolymorphicServiceFactory"
- CodeBehind="PolymorphicService.svc.cs" %>
And that’s it, the service is set. In the full code for this post I’ve also added a test project which uses the same resolver to send instances of the derived types to the service.
My (personal) favorite scenario: single file bug reproductions
This scenario may not apply to most people, but as a tester in the WCF team when I find a bug in the product I try to isolate it as much as possible, and create a scenario which reproduces it as easily as possible. In some IIS-specific bugs, I used to create a whole project (and IIS application) for the issue, and that ended up causing my IIS to have a very large number of applications (when the product is in development there are many bugs which are found and fixed). Also, for simple tests I used to have one single application with many services, and the web.config file was becoming quite large.
That’s when I discovered (a couple of years back) the service host factory. Then I realized I didn’t need config at all, and that every thing be done in a single file. I could, for my small tests, simply drop a file in my ZeroConfig application root. And to tell other people how to reproduce a certain bug, I could just send them a text file (notepad-powered development), and they could drop that file in any IIS application and verify the problem right away. For example, this is a known issue in WCF that if you have an operation with a Stream parameter, and another bound to the UriTemplate in a WCF HTTP endpoint (i.e., a file upload operation), but you still enable the metadata retrieval (which doesn’t work for Web endpoints, but that’s another story), then browsing to the service page will show an error, even though the upload function is working without any issues. To reproduce it, there’s no need to change config files, deployments – simply drop this file on an IIS application, browse to it and the error will show up right away.
- <%@ ServiceHost Language="C#" Debug="true" Service="MyService"
- Factory="MyFactory" %>
- using System;
- using System.IO;
- using System.ServiceModel;
- using System.ServiceModel.Activation;
- using System.ServiceModel.Description;
- using System.ServiceModel.Web;
- [ServiceContract]
- public class MyService
- {
- [WebInvoke(UriTemplate = "/Upload/{fileName}")]
- public void Upload(string fileName, Stream fileContents) { }
- }
- public class MyFactory : ServiceHostFactory
- {
- protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
- {
- ServiceHost host = new ServiceHost(serviceType, baseAddresses);
- host.AddServiceEndpoint(typeof(MyService), new WebHttpBinding(), "")
- .Behaviors.Add(new WebHttpBehavior());
- ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
- smb.HttpGetEnabled = true;
- host.Description.Behaviors.Add(smb);
- ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
- sdb.IncludeExceptionDetailInFaults = true;
- return host;
- }
- }
Comments
- Anonymous
April 05, 2013
Excellent article! - Anonymous
June 09, 2014
Hi!I have implemented your solution "Via the @ServiceHost directive".<%@ ServiceHost Language="C#" Debug="true" Service="API.JSTest" Factory="API.JSTestFactory" CodeBehind="~/App_Code/API/JSTest.cs" %>public class JSTestFactory : ServiceHostFactory
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]{ protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { WebServiceHost host = new WebServiceHost(serviceType, myAddress); host.AddServiceEndpoint(typeof(API.IJSTest), new WebHttpBinding(), ""); return host; }}
The service works - I consumed it from javascript, and it also works when I call http://.../api/jstest.svc/GetTest/mystringThe thing is that when I put the service http://.../api/jstest.svc in the browser address bar I get " Endpoint not found."I also get this Warning in svclog : The incoming HTTP request's URI 'http://.../api/JSTest.svc' does not match any service operation.Do you know why? Thank you[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]public class JSTest : IJSTest{ public string GetTest(string a) { return a; }}[ServiceContract]public interface IJSTest{ [OperationContract] [WebGet(UriTemplate = "GetTest/{a}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetTest(string a);}
- Anonymous
July 17, 2015
Very good article. Gives a lot of clarity on servicehostfactory