My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
WcfIntegration  
Autofac can host services in a WCF server, and can increase the reliability of WCF clients.
Autofac2
Updated Mar 10, 2012 by alex.meyergleaves

Getting Started

Autofac.Integration.Wcf.dll is included in the Autofac binary downloads. To use the integration you need to reference it as well as Autofac.dll in your project.

Clients

The proxy objects returned from ChannelFactory.CreateChannel<T>() need to be disposed before client and server resources assigned to the channel are freed. This is where Autofac's deterministic disposal really shines.

To take advantage of this, register and use your channel factories through a container client-side.

Registration

Something like the following needs to run during application startup:

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 to 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);
  }
}

If you haven't read up on nested containers, you might want to check out the documentation now.

using(var lifetime = container.BeginLifetimeScope())
{
  var albumPrinter = inner.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.

Servers Hosted in IIS

To use the integration from an IIS-hosted application, reference both Autofac.dll and Autofac.Integration.Wcf.dll.

After that, the pattern is the same:

  1. In your global application startup...
    1. Build a Container where your service type is registered.
    2. Set AutofacHostFactory.Container with this built container.
  2. Update your .svc files to use the AutofacServiceHostFactory (for BasicHttpBinding or WSHttpBinding services) or the AutofacWebServiceHostFacotry (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" %>

Self-Hosting

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);
}

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".

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 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")); 

Resources

See the original discussion thread.

Comment by rodgarci...@gmail.com, Oct 6, 2008

Hi Nicholas, I have a problem with the binding configuration. This config above has no effect, do you know why?

            // AssinadorService
            builder.Register(c => new ChannelFactory<IAssinadorService>(
                new BasicHttpBinding()
                {
                    CloseTimeout = new TimeSpan(0, 5, 0),
                    OpenTimeout = new TimeSpan(0, 5, 0),
                    ReceiveTimeout = new TimeSpan(0, 10, 0),
                    SendTimeout = new TimeSpan(0, 10, 0),
                    AllowCookies = false,
                    BypassProxyOnLocal = true,
                    HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
                    MaxBufferSize = int.MaxValue,
                    MaxBufferPoolSize = int.MaxValue,
                    MaxReceivedMessageSize = int.MaxValue,
                    MessageEncoding = WSMessageEncoding.Text,
                    TextEncoding = System.Text.Encoding.UTF8,
                    TransferMode = TransferMode.Buffered,
                    UseDefaultWebProxy = true
                },
                new EndpointAddress(ConfigurationManager.AppSettings["AssinadorServiceUrl"])));
            builder.Register(c => c.Resolve<ChannelFactory<IAssinadorService>>().CreateChannel() ).FactoryScoped();
            builder.Register<NfeUtilV1>().OnActivating(ActivatingHandler.InjectProperties).WithScope(Autofac.InstanceScope.Factory);

            Autofac.Integration.Wcf.AutofacServiceHostFactory.Container = builder.Build();

Thanks,

Rodrigo Lima

Comment by rodgarci...@gmail.com, Oct 6, 2008

I Find a solution:

// AssinadorService
            builder.Register(c => new ChannelFactory<IAssinadorService>("basic")); // endpointConfigurationName
            builder.Register(c => c.Resolve<ChannelFactory<IAssinadorService>>().CreateChannel() ).FactoryScoped();
            builder.Register<NfeUtilV1>().OnActivating(ActivatingHandler.InjectProperties).WithScope(Autofac.InstanceScope.Factory);

            Autofac.Integration.Wcf.AutofacServiceHostFactory.Container = builder.Build();
Comment by marko.po...@gmail.com, Oct 24, 2008

I would be very interested in something like System.ServiceModel?.Activation.WebScriptServiceHostFactory?, which automatically adds ASP.NET AJAX endpoint to a service (it serializes to JSON, not to SOAP), but is implemented with AutoFac? and supports property injection (like the one in Pages) and some transaction management (so I could add an attribute Transactional? to the method.

Comment by matt.wis...@gmail.com, Mar 24, 2009

I would like to point out that

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

is for basicHttpBinding and wsHttpbinding (SOAP web services).

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

Works for webHttpBinding (RESTful web services).

Comment by felix.wo...@gmail.com, Feb 17, 2010

hi, i have a problem using autofac with wcf. If i use the example of this site all are correct. If i create a new wcf project in my visualstudio 2010 beta 2 with framework 4.0 and recreate from scratch the same example i have this error: The service 'Service1' configured for WCF is not registered with the Autofac container.

stack trace: Autofac.Integration.Wcf.AutofacHostFactory?.CreateServiceHost?(String constructorString, Uri baseAddresses) in D:\projects\Autofac-2.1.10.754-NET40\autofac\src\Source\Autofac.Integration.Wcf\AutofacHostFactory?.cs:75

Can anyone help me?

Thanks felix

Comment by project member nicholas...@gmail.com, Feb 17, 2010

Hi Felix, it looks like you need to use the fully-qualified name of your service in the .SVC file; i.e. "TestService?.Service1, TestService?" - HTH, Nick

Comment by felix.wo...@gmail.com, Feb 18, 2010

thanks, now works! felix

Comment by ryan.smi...@gmail.com, Feb 18, 2010

The WCF example here appears to rely on the IDisposable interface of the client channel, which is generally considered broken (http://msdn.microsoft.com/en-us/library/aa355056.aspx).

Comment by ecof...@gmail.com, Mar 12, 2010

When attempting to host a WCF Autofac Integrated Service in IIS (for backing a Ajax page) I get a Security Exception about partial trust.

Details here: http://stackoverflow.com/questions/2429541/autofac-wcf-integration-security-problem

Anyone else seeing this behavior?

Comment by oliver.friedrich@gmail.com, Jul 28, 2010

I seem to be missing a piece in the puzzle: how does Autofac actually inject any service instances into my WCF service? Are they being passed in the constructor or are they injected as properties?

Maybe this question is obsolete once I find out why my Service is initialized by the System.ServiceModel.Dispatcher.InstanceBehavior.GetInstance() method and not the AutofacWebServiceHostFactory... (this is what I see when I debug the WCF service).

Can anybody provide me with the missing info? This is all in Autofac 1.4.5.

Thanks in advance, Oliver

P.S. Just cross-posted my question with more info here: http://stackoverflow.com/questions/3355937/using-autofac-for-di-into-wcf-service-hosted-in-asp-net-application

Comment by adam.c...@gmail.com, Mar 18, 2011

Is there any work around to having to include the fully-qualified name of the service in the .svc file? I want to target a different assembly based on build definition and having to specify the assembly name in the svc file makes this difficult.

Comment by adam.c...@gmail.com, Mar 18, 2011

I am now using configuration based activation and different config files depending upon the build definition. This gives me what I wanted.

Comment by sebastia...@gmail.com, Jun 11, 2011

Hi!

Earlier in the wiki I've found: WCF integrations are set up so that InstancePerLifetimeScope?() will attach a component to the current web request or service method call.

This lifetime is too short for me. I'm using WCF web services in session mode:

[ServiceBehaviorAttribute(InstanceContextMode=InstanceContextMode.PerSession)]
public class MyService : IMyService
{
  [...]
}

In the session context (actually the service instance) I've some state from preceding service calls. Other components and subsystems created on the fly in an unit of work context (per service method call) have dependencies on the components created (as single instance) in the session context. I would like to safe the session state in the container and inject it to underlying services.

How can I solve this problem with autofac and WCF integrations?

Comment by mauleshr...@gmail.com, Jun 23, 2011

Hi,

I am using autofac wcf integration in our web service project. I am experiencing a problem when the same instance of the WCF service is served back to a new request. With the first web request autofacWcfServiceHostFactory comes in to provide the service instance. The service instance has a sessionId attached to it. I recorded the session id for my reference. In my next request I get the same session id with the same dependency objects. All my WCF services are per call services. Can you throw some light on why am I getting same instance or am I looking at the wrong place. How does the object gets released in this case.

Thank You!

Comment by songzi...@gmail.com, Aug 29, 2011
using(var lifetime = container.BeginLifetimeScope())
{
  var albumPrinter = inner.Resolve<AlbumPrinter>();
  albumPrinter.PrintTracks("The Shins", "Wincing the Night Away");
}

should be

using(var lifetime = container.BeginLifetimeScope())
{
  var albumPrinter = lifetime.Resolve<AlbumPrinter>();
  albumPrinter.PrintTracks("The Shins", "Wincing the Night Away");
}
Comment by wi...@netindustry.nl, Sep 16, 2011

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

What would exactly be the preferred method if I have a WCF Web Application? Application_Start? doesn't seem to fire with SVC only projects, any ideas?

Comment by songzi...@gmail.com, Jan 23, 2012

I am really happy to know why you have to use DI for WCF? How can you benefit from it? Don't use technology for the sake of that. Too much DI can only lead to maintenance issue even DI is supposed to alleviate it but actually aggrieve.

Comment by dhawa...@gmail.com, May 10, 2012

I want to do something similar to what u did in the Self-Hosting example i.e. pass a Logger to my service and also add a custom ServiceBehavior?. But I want to pass the logger instance to the custom ServiceBehavior?, which then passes the Logger instance to my ErrorHandler? class. Also, my service is hosted in IIS so I cannot create a new ServiceHost?. I'm assuming Autofaq creates the service host. How do I add the custom ServiceBehaior? and pass a logger instance to it in my case?


Sign in to add a comment
Powered by Google Project Hosting