You can add the interceptors dynamically but it requires a little work. The way to go is to create a custom Autofac.Module
that attaches to all component registrations. I'll show it to you in an example.
You can't really do EnableInterfaceInterceptors
globally. I'll get to that at the end of the example.
First, the example setup: We have a simple interface, a simple implementation, and an interceptor that will handle logging calls. (I'm stealing the interceptor code from the Autofac wiki):
public interface IInterface
{
void DoWork();
}
public class Implementation : IInterface
{
public void DoWork()
{
Console.WriteLine("Implementation doing work.");
}
}
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.WriteLine("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
We want to intercept everything (that has interceptors enabled) with our call logger. We do that by creating a custom Autofac.Module
that both registers the interceptor itself with Autofac and attaches to component registrations on the fly to add the interceptor metadata.
Warning: There's a little hacking here that works but is sort of "poking data" into a sort-of-known-location. It works and I don't know why it'd change, but be aware that because it's sorta-kinda working on "private-ish" stuff, this may break in a future release. Just be aware.
OK, disclaimer done. Here's the module:
public class InterceptorModule : Autofac.Module
{
// This is a private constant from the Autofac.Extras.DynamicProxy2 assembly
// that is needed to "poke" interceptors into registrations.
const string InterceptorsPropertyName = "Autofac.Extras.DynamicProxy2.RegistrationExtensions.InterceptorsPropertyName";
protected override void Load(ContainerBuilder builder)
{
// Register global interceptors here.
builder.Register(c => new CallLogger(Console.Out));
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
// Here is where you define your "global interceptor list"
var interceptorServices = new Service[] { new TypedService(typeof(CallLogger)) };
// Append the global interceptors to any existing list, or create a new interceptor
// list if none are specified. Note this info will only be used by registrations
// that are set to have interceptors enabled. It'll be ignored by others.
object existing;
if (registration.Metadata.TryGetValue(InterceptorsPropertyName, out existing))
{
registration.Metadata[InterceptorsPropertyName] =
((IEnumerable<Service>)existing).Concat(interceptorServices).Distinct();
}
else
{
registration.Metadata.Add(InterceptorsPropertyName, interceptorServices);
}
}
}
To make it work, you register the module along with the rest of your dependencies. For this example, that'd look like:
var builder = new ContainerBuilder();
// Notice this registration doesn't include
// the interceptor - that gets added by the
// module.
builder.RegisterType<Implementation>()
.As<IInterface>()
.EnableInterfaceInterceptors();
// Here's the magic module:
builder.RegisterModule<InterceptorModule>();
var container = builder.Build();
If you run these registrations and resolve...
var impl = container.Resolve<IInterface>();
impl.DoWork();
You can see the interceptor works as you'll see the console output:
Calling method DoWork with parameters ...
Implementation doing work.
Done: result was .
(It's a little odd because I have a parameterless/void method in my example, but the interceptor is working!)
As for the EnableInterfaceInterceptors
call... Doing EnableInterfaceInterceptors
or EnableClassInterceptors
actually does a lot of crazy DynamicProxy2 work on the back end. It adds some non-trivial event handlers to the activation event on your component that wrap the object in a dynamic proxy. These event handlers are not currently exposed for separate usage and I'm not sure how much work it'd be to try and attach all these things "after the fact" the way we're doing here with the actual interceptors.
You are welcome to give it a try yourself - the source is on GitHub. But, basically, while the "add a global interceptor" thing works, doing global EnableInterfaceInterceptors
in a module is well off the beaten trail. You'd definitely be on your own for that one.