-
Notifications
You must be signed in to change notification settings - Fork 0
Wcf Integration
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.
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.
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.
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.
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:
- In your global application startup...
- Build a
Container
where your service type is registered. - Set
AutofacHostFactory.Container
with this built container. - Update your .svc files to use the
AutofacServiceHostFactory
(forBasicHttpBinding
orWSHttpBinding
services) or theAutofacWebServiceHostFactory
(forWebHttpBinding
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.
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")
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")
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" %>
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
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".
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);
}
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
.
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
.
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"));