Skip to content
alexmg edited this page Jan 22, 2014 · 1 revision

Autofac can host services in a WCF server, and can increase the reliability of WCF clients.

Getting Started

WCF integration is in the Autofac.Integration.Wcf.dll assembly. This is in the Autofac.Integration.Wcf NuGet package.

Autofac WCF support is available for...

There is also some special information around using single-instance services.

Clients

There are a couple of benefits to using Autofac in conjunction with your service client application:

  • Deterministic disposal: Automatically free resources consumed by proxies created by ChannelFactory.CreateChannel<T>().
  • Easy service proxy injection: For types that consume services you can easily inject a dependency on the service interface type.

Registration

During application startup, for each service register a ChannelFactory<T> and a function that uses the factory to open channels:

var builder = new ContainerBuilder();

builder.Register(c => new ChannelFactory<ITrackListing>(
    new BasicHttpBinding(),
    new EndpointAddress("http://localhost/TrackListingService")))
  .SingleInstance();

builder.Register(c => c.Resolve<ChannelFactory<ITrackListing>>().CreateChannel())
  .UseWcfSafeRelease();

// Another application class not using WCF
builder.RegisterType<AlbumPrinter>();

var container = builder.Build();

The Register() method infers the ITrackListing service type from the return value of the expression, which is the result of channelFactory.CreateChannel(). The call to CreateChannel() isn't executed until ITrackListing is requested from the container.

The UseWcfSafeRelease() configuration option ensures that exception messages are not lost when disposing client channels.

Using the Service

Our application prints a track listing to the console using the remote ITrackListing service. It does this via the AlbumPrinter class:

class AlbumPrinter
{
  readonly ITrackListing _trackListing;

  public AlbumPrinter(ITrackListing trackListing)
  {
    _trackListing = trackListing;
  }

  public void PrintTracks(string artist, string album)
  {
    foreach (var track in _trackListing.GetTracks(artist, album))
      Console.WriteLine("{0} - {1}", track.Position, track.Title);
  }
}

To consume the service, resolve the consuming class from an Autofac lifetime scope. (If you haven't read up on Deterministic Disposal, you might want to check out the documentation now.) Note if you're in an ASP.NET application (AspNetIntegration, MvcIntegration), this will most likely be the automatically-generated request lifetime scope.

using(var lifetime = container.BeginLifetimeScope())
{
  var albumPrinter = lifetime.Resolve<AlbumPrinter>();
  albumPrinter.PrintTracks("The Shins", "Wincing the Night Away");
}

The important thing to note in this small example is that neither the client code of the album printer, nor the album printer itself, has to be aware that the ITrackListing service is in fact a WCF service and needs to be disposed. The container takes care of this housekeeping automatically when inner is disposed by the using block.

IIS Hosted Services

To use the integration from an IIS-hosted application (including WAS hosting), reference both Autofac.dll and Autofac.Integration.Wcf.dll.

After that, the pattern is similar to AspNetIntegration or MvcIntegration:

  1. In your global application startup...
  2. Build a Container where your service type is registered.
  3. Set AutofacHostFactory.Container with this built container.
  4. Update your .svc files to use the AutofacServiceHostFactory (for BasicHttpBinding or WSHttpBinding services) or the AutofacWebServiceHostFactory (for WebHttpBinding services).

The choice you have is in how you want to register your services and specify the target in your .svc files.

An example is included in the /trunk/Examples folder.

Implementation Type Registration

Your first option is to simply register the service implementation type in the container and specify that implementation type in the .svc file. This is the most common usage.

In your application startup, you'd have code like this:

var builder = new ContainerBuilder();
builder.RegisterType<TestService.Service1>();
AutofacHostFactory.Container = builder.Build();

And your .svc file would specify the appropriate service implementation type and host factory, like this:

<%@ ServiceHost
    Service="TestService.Service1, TestService"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

(Note that you need to use the fully-qualified name of your service in the .svc file, i.e Service="Namespace.!ServiceType, !AssemblyName")

Contract Type Registration

Your second option is to register the contract type in the container and specify the contract in the .svc file. This is handy if you don't want to change the .svc file but do want to change the implementation type that will handle requests.

In your application startup, you'd have code like this:

var builder = new ContainerBuilder();
builder.RegisterType<TestService.Service1>().As<TestService.IService1>();
AutofacHostFactory.Container = builder.Build();

And your .svc file would specify the service contract type and host factory, like this:

<%@ ServiceHost
    Service="TestService.IService1, TestService"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

(Note that you need to use the fully-qualified name of your contract in the .svc file, i.e Service="Namespace.!IContractType, !AssemblyName")

Named Service Registration

The final option you have is to register a named service implementation in the container and specify that service name in the .svc file. This is handy if you want even further abstraction away from the .svc file.

In your application startup, you'd have code like this:

var builder = new ContainerBuilder();
builder.RegisterType<TestService.Service1>().Named<object>("my-service");
AutofacHostFactory.Container = builder.Build();

Note that the service implementation type is registered as an object - this is important. Your service implementation won't be found if it's a named service and it's not registered as an object.

Your .svc file specifies the service name you registered and host factory, like this:

<%@ ServiceHost
    Service="my-service"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

WAS Hosting Extensionless Services

Register your service using an Implementation Type Registration.

var builder = new ContainerBuilder();
builder.RegisterType<Service1>();
AutofacHostFactory.Container = builder.Build();

Define a new ServiceRoute using the AutofacServiceHostFactory and service implementation type.

RouteTable.Routes.Add(new ServiceRoute("Service1", new AutofacServiceHostFactory(), typeof(Service1)));

Add a factory entry under the serviceActivation element in the web.config file. This ensures that the AutofacServiceHostFactory is used to activate the service.

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
  <serviceActivations>
    <add factory="Autofac.Integration.Wcf.AutofacServiceHostFactory"
         relativeAddress="~/Service1.svc"
         service="TestService.Service1" />
  </serviceActivations>
</serviceHostingEnvironment>

Add the UrlRoutingModule to the web.config file.

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </modules>
  <handlers>
    <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" />
  </handlers>
</system.webServer>

After configuring your application in IIS you will be able to access the WCF service at: http://hostname/appname/Service1

WAS Hosting and Non-HTTP Activation

When hosting WCF Services in WAS (Windows Activation Service), you are not given an opportunity to build your container in the Application_Start event defined in your Global.asax because WAS doesn't use the standard ASP.NET pipeline.

The alternative approach is to place a code file in your App_Code folder that contains a type with a public static void AppInitialize() method.

namespace MyNamespace
{
  public static class AppStart
  {
    public static void AppInitialize()
    {
      // Put your container initialization here.
    } 
  }
}

You can read more about AppInitialize() in "How to Initialize Hosted WCF Services".

Self-Hosted Services

To use the integration when self-hosting your WCF Service, reference both Autofac.dll and Autofac.Integration.Wcf.dll. Below is a sample of a web service that has a constructor dependency on an ILogger instance.

The interface and implementation for the logger that will be the dependency.

public interface ILogger
{
    void Write(string message);
}

public class Logger : ILogger
{
    public void Write(string message)
    {
        Console.WriteLine(message);
    }
}

The contract and implementation for the web service.

[ServiceContract]
public interface IEchoService
{
    [OperationContract]
    string Echo(string message);
}

public class EchoService : IEchoService
{
    private readonly ILogger _logger;

    public EchoService(ILogger logger)
    {
        _logger = logger;
    }

    public string Echo(string message)
    {
        _logger.Write(message);
        return message;
    }
}

The Console Application code for building the container and hosting the web service.

ContainerBuilder builder = new ContainerBuilder();
builder.Register(c => new Logger()).As<ILogger>();
builder.Register(c => new EchoService(c.Resolve<ILogger>())).As<IEchoService>();

using (IContainer container = builder.Build())
{
    Uri address = new Uri("http://localhost:8080/EchoService");
    ServiceHost host = new ServiceHost(typeof(EchoService), address);

    host.AddServiceEndpoint(typeof(IEchoService), new BasicHttpBinding(), string.Empty);

    host.AddDependencyInjectionBehavior<IEchoService>(container);

    host.Description.Behaviors.Add(new ServiceMetadataBehavior {HttpGetEnabled = true, HttpGetUrl = address});
    host.Open();
    
    Console.WriteLine("The host has been opened.");
    Console.ReadLine();

    host.Close();
    Environment.Exit(0);
}

Handling !InstanceContextMode.Single Services

Using InstanceContextMode.Single is not a good idea from a scalability point of view, and allowing multiple callers to access the single instance using ConcurrencyMode.Multiple means that you also need to be careful about multiple threads accessing any shared state. If possible you should create services with InstanceContextMode.PerCall.

IIS/WAS Hosted

The AutofacServiceHostFactory identifies WCF services that are marked with InstanceContextMode.Single and will ensure that the ServiceHost can be provided with a singleton instance from the container. An exception will be thrown if the service in the container was not registered with the SingleInstance lifetime scope. It is also invalid to register a SingleInstance service in the container for a WCF service that is not marked as InstanceContextMode.Single.

Self-Hosted

It is possible to manually perform constructor injection for service marked with InstanceContextMode.Single when self-hosting. This is achieved by resolving a SingleInstance service from the container and then passing that into the constructor of a manually created ServiceHost.

// Get the SingleInstance from the container.
IEchoService echoService = container.Resolve<IEchoService>(); 
// Pass it into the ServiceHost preventing it from creating an instance with the default constructor.
ServiceHost host = new ServiceHost(echoService, new Uri("http://localhost:8080/EchoService")); 
Clone this wiki locally