Plug and Play Services with MEF and WCF

It’s been a while since I’ve blogged any MEF stuff, and having never blogged anything to do with WCF, I thought it could be good to share something I’ve been playing with these last few days.

The Premise
I’m already using MEF now in quite a few production projects, but also a lot of work lately has been going in to evolve the architecture of a service bus. Consider a set of individual services hosted as seperate WCF services. The hosting architecture is all essentially the same.

Where does MEF fit in? What I wanted to do was build an architecture that will allow me to drop in libraries that contain WCF services, and we’ll discover them with MEF and instantiate them.

The Basics
To aid discovery of services via MEF, we need someway of expressing that the service is actually a service, instead of just another part. Now, typically when we are exporting a part, we would label up a part, either using a common base type, or just with an empty export:

[Export]
[Export(typeof(ISomething))]
What I decided on doing, was provide a marker interface. This will allow us to select the all service parts from the container. Let’s look at the contract, and a sample service that might implement it:

public interface IHostedService { } [ServiceContract] public interface ICalculatorService : IHostedService { [OperationContract] int Add(int operandA, int operandB); }


Now when we export our service, we also need a way of describing that service to our consuming types. So, that means some metadata, here’s what I’ve got:

public interface IHostedServiceMetadata { string Name { get; } Type ServiceType { get; } }
Code language: JavaScript (javascript)


The metadata interface is quite simplistic, giving us a way of selecting the service via a name. The ServiceType property I’ll come to a bit later. Now we can wrap up the export and the metadata, by combining them into a single export attribute:

public interface IHostedServ[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true), MetadataAttribute] public class ExportServiceAttribute : ExportAttribute, IHostedServiceMetadata { public ExportServiceAttribute(string name, Type serviceType) : base(typeof(IHostedService)) { Name = name; ServiceType = ServiceType; } public string Name { get; private set; } public Type ServiceType { get; private set; } }

MEF is extensible by design, and a core part of that is the ExportProvider model. ExportProvider instances are used to match ImportDefinitions with appropriate parts. So in this sense, we can create an ExportProvider that will automatically create ServiceHost instances.

public class ServiceHostExportProvider : ExportProvider { private static readonly string MatchContractName = AttributedModelServices .GetContractName(typeof(ExportServiceHost)); public CompositionContainer SourceContainer { get; set; } protected override IEnumerable<Export> GetExportsCore( ImportDefinition importDefinition, AtomicComposition atomicComposition) { if (importDefinition.ContractName.Equals(MatchContractName)) { var exports = SourceContainer .GetExports<IHostedService, IHostedServiceMetadata>(); Func<IHostedServiceMetadata, Export> factory = m => new Export(MatchContractName, () => ExportServiceHostFactory.CreateExportServiceHost(SourceContainer, m)); switch (importDefinition.Cardinality) { case ImportCardinality.ExactlyOne: { var export = exports.Single(); return new[] { factory(export.Metadata) }; } case ImportCardinality.ZeroOrOne: { var export = exports.SingleOrDefault(); return (export == null) ? Enumerable.Empty<Export>() : new[] { factory(export.Metadata) }; } case ImportCardinality.ZeroOrMore: { return exports.Select(e => factory(e.Metadata)); } } } return Enumerable.Empty<Export>(); } }
Code language: HTML, XML (xml)


Now, remember I added that ServiceType property to our metadata export, the reason being is we need that to create or service, and its not easy to grab Type information from MEF, because MEF is designed for the discovery of unknown parts (in the default programming model). So our ServiceType property allows us to express the actual type before we’ve created the instance.

The other issue we have, is the design of the ServiceHost type that System.ServiceModel provides. ServiceHost allows us to pass a Singleton object, or a Type instance. MEF supports the creation of types with complex constructors with dependency injection, but the ServiceHost(Type) constructor enforces a public default constructor. What I decided to do, is create a custom service host type, the ExportServiceHost which will support custom creation of objects via MEF.

The ExportServiceHost type extends ServiceHostBase, with the main requirement being to initialise the ServiceDescription which actually describes our service:

protected override ServiceDescription CreateDescription( out IDictionary implementedContracts) { var sd = new ServiceDescription { ServiceType = Meta.ServiceType }; implementedContracts = GetContracts(Meta.ServiceType) .ToDictionary(cd => cd.ConfigurationName, cd => cd); var endpointAttributes = GetEndpoints(Meta.ServiceType); foreach (var cd in implementedContracts.Values) { foreach (var endpoint in GetServiceEndpoints(endpointAttributes, meta, cd)) sd.Endpoints.Add(endpoint); } var serviceBehaviour = EnsureServiceBehavior(sd); serviceBehaviour.InstanceContextMode = InstanceContextMode.PerSession; foreach (var endpointAttribute in endpointAttributes) endpointAttribute.UpdateServiceDescription(sd); AddBaseAddresses(sd.Endpoints); return sd; }
Code language: PHP (php)


There is no wiring up at this point, we need to extend WCF with an IInstanceProvider and a behaviour to link it through. We add this through our factory class:

internal static class ExportServiceHostFactory { public static ExportServiceHost CreateExportServiceHost( CompositionContainer container, IHostedServiceMetadata meta) { var host = new ExportServiceHost(meta); host.Description.Behaviors.Add( new ExportServiceBehavior(container, meta.Name)); return host; } }
Code language: PHP (php)


One of the key parts of this implementation is how we describe our endpoints.

The Endpoints
One of the early pain points when learning WCF is getting used to configuration of endpoints and bindings. With the idea of dynamic services, we should try and rely on configuration less. While this could be objectional, it isn’t too different from say, a fluent configuration API.

What we can take advantage of is the ability to decorate our service implementation with attributes which define both endpoints/bindings. Now, I imaging we can leverage a base attribute with common endpoint properties, and then specialise for our different communication schemes, http, tcp, etc.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public abstract class EndpointAttribute : Attribute { protected EndpointAttribute(int defaultPort) { Port = defaultPort; } public string BindingConfiguration { get; set; } public string Path { get; set; } public int Port { get; set; } internal abstract ServiceEndpoint CreateEndpoint( ContractDescription description, IHostedServiceMetadata meta); protected virtual Uri CreateUri(string scheme, IHostedServiceMetadata) { var builder = new UriBuilder(scheme, "localhost", Port, Path ?? meta.Name); return builder.Uri; } }

So, let’s have a look at a specialised endpoint attribute:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class HttpEndpointAttribute : EndpointAttribute { private const int DefaultPort = 50001; public HttpEndpointAttribute() : base(DefaultPort) { EnableGet = true; } public HttpBindingType BindingType { get; set; } public bool UseHttps { get; set; } public bool EnableGet { get; set; } internal override ServiceEndpoint CreateEndpoint( ContractDescription description, IHostedServiceMetadata meta) { var uri = CreateUri((UseHttps) ? "https" : "http", meta); var address = new EndpointAddress(uri); var binding = CreateBinding(BindingType); return new ServiceEndpoint(description, binding, address); } protected virtual Binding CreateBinding(HttpBindingType bindingType) { switch (bindingType) { case HttpBindingType.BasicHttp: return (BindingConfiguration == null) ? new BasicHttpBinding() : new BasicHttpBinding(BindingConfiguration); case HttpBindingType.WSHttp: return (BindingConfiguration == null) ? new WSHttpBinding() : new WSHttpBinding(BindingConfiguration); default: throw new ArgumentNullException("Unsupported binding type: " + bindingType); } } [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class HttpEndpointAttribute : EndpointAttribute { private const int DefaultPort = 50001; public HttpEndpointAttribute() : base(DefaultPort) { EnableGet = true; } public HttpBindingType BindingType { get; set; } public bool UseHttps { get; set; } public bool EnableGet { get; set; } internal override ServiceEndpoint CreateEndpoint( ContractDescription description, IHostedServiceMetadata meta) { var uri = CreateUri((UseHttps) ? "https" : "http", meta); var address = new EndpointAddress(uri); var binding = CreateBinding(BindingType); return new ServiceEndpoint(description, binding, address); } protected virtual Binding CreateBinding(HttpBindingType bindingType) { switch (bindingType) { case HttpBindingType.BasicHttp: return (BindingConfiguration == null) ? new BasicHttpBinding() : new BasicHttpBinding(BindingConfiguration); case HttpBindingType.WSHttp: return (BindingConfiguration == null) ? new WSHttpBinding() : new WSHttpBinding(BindingConfiguration); default: throw new ArgumentNullException("Unsupported binding type: " + bindingType); } } }

The Examples
Right, so let’s put this all together, using our sample contract we defined earlier:

[ExportService("SimpleCalculator", typeof(SimpleCalculatorService)), HttpEndpoint] public class SimpleCalculatorService : ICalculatorService { public int Add(int operandA, int operandB) { return (operandA + operandB); } }

As you can see, the idea is simple, you can focus on your service implementation, and the MEF architecture just gets out of the way. To simply it further, we can change our architecture to automatically create a HttpEndpointAttribute where no EndpointAttribute instances are defined.

A more advanced example, let’s add in support for a complex constructor:

[ExportService("AdvancedCalculator", typeof(AdvancedCalculatorService)), HttpEndpoint, TcpEndpoint] public class AdvancedCalculatorService : ICalculatorService { private readonly ILogger _logger; [ImportingConstructor] public AdvancedCalculatorService(ILogger logger) { _logger = logger; _logger.Log("Created instance of AdvancedCalculatorService"); } public int Add(int operandA, int operandB) { int result = (operandA + operandB); _logger.Log("Computing result: " + operandA + " + " + operandB + ": " + result); return result; } }


We get the benefits of clean, testable code, and dynamic instantiation with dependency injection for our services.

The Web
When hosting services through IIS, the server handles requests to .svc files. To support our dynamic instantiation, we can use a derivative of Darko’s Dynamic IIS hosted WCF Service work. In which we create a custom ServiceHostFactory, and then power it through a VirtualPathProvider. Our WebServiceHostFactory is shaped like:

public class WebServiceHostFactory : ServiceHostFactory { private static CompositionContainer _container; private static readonly sync = new object(); public CompositionContainer Container { get { lock (object) { return _container; } } } public override ServiceHostBase CreateServiceHost( string constructorString, Uri[] baseAddresses) { var meta = Container .GetExports<IHostedService, IHostedServiceMetadata>() .Where(e => e.Metadata.Name.Equals(constructorString, StringComparison.OrdinalIgnoreCase)) .Select(e => e.Metadata) .SingleOrDefault(); if (meta == null) return null; var host = new ExportServiceHost(meta, baseAddresses); host.Description.Behaviors.Add( new ExportServiceBehavior(Container, meta.Name)); var contracts = meta.ServiceType .GetInterfaces() .Where(t => t.IsDefined(ServiceContractAttribute), true)); EnsureHttpBinding(host, contracts); return host; } private static void EnsureHttpBinding( ExportServiceHost host, IEnumerable<Type> contracts) { var binding = new BasicHttpBinding(); host.Description.Endpoints.Clear(); foreach (var contract in contracts) host.AddServiceEndpoint(contract.FullName, binding, ""); } }
Code language: HTML, XML (xml)

Now we are in a position to dynamically support WCF services through both standalone code (e.g. windows services, console apps, etc.), and also hosted code (ASP.NET). Please find the sample code attached, which includes the base code, some example services, some example hosts, and also an example client.

I welcome any and all feedback

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Post