Closed roald-di closed 7 years ago
Was there a reason you couldn't use the existing registration mechanism to pass parameters?
builder.RegisterType<CachingInterceptor>()
.WithParameter(TypedParameter.From(new InMemoryCache()))
.Named<IInterceptor>("cache");
builder.RegisterType<Service>()
.As<IService>()
.EnableInterfaceInterceptors()
.InterceptedBy("cache");
Even if you need different parameters on a per interceptor basis, you could register them with different names.
What I was trying to do was to create an extension method on a registration that would enable caching but still allow for some configuration, something like this:
builder.RegisterType<Service>()
.Cache(config =>
{
config.ForMethod(service => service.GetData()).Expires([..]);
});
Now I agree with you that the same end result could still be obtained by putting the extension on the builder instead and passing in a name to wire things up, however I would find that a little awkward to use and harder not to make a mistake.
This is more a convenience feature than anything groundbreaking but I still wanted this.. :blush:
I think you could still do something really close to what you're looking for with a custom extension using stuff out of the box. Here's a full working console app example showing what I mean:
using System;
using System.IO;
using Autofac;
using Autofac.Builder;
using Autofac.Extras.DynamicProxy2;
using Castle.DynamicProxy;
namespace InterceptorExample
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.Register(ctx => Console.Out).As<TextWriter>();
// This shows how the extension would be called - very similar
// to the way your example is described. Easy to modify the
// semantics to take an Action<Options> instead of a concrete
// parameter value.
builder.RegisterType<Worker>().LogCalls(builder, "a");
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var worker = scope.Resolve<Worker>();
worker.DoWork();
}
Console.ReadKey();
}
}
// Super simple interceptor based on the doc examples
public class CallLogger : IInterceptor
{
TextWriter _output;
string _prefix;
public CallLogger(TextWriter output, string prefix)
{
_prefix = prefix;
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.WriteLine("{0}: calling {1}", _prefix, invocation.Method.Name);
invocation.Proceed();
}
}
// Super simple class to intercept
public class Worker
{
TextWriter _writer;
public Worker(TextWriter writer)
{
_writer = writer;
}
public virtual void DoWork()
{
_writer.WriteLine("Doing work.");
}
}
public static class InterceptionExtensions
{
public static IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>
LogCalls<TLimit, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle> registration,
ContainerBuilder builder,
string prefix)
{
// Register a different unique interceptor for each wire-up so you can
// provide different parameters as needed.
var interceptorId = Guid.NewGuid().ToString();
registration.EnableClassInterceptors().InterceptedBy(interceptorId);
// You need the builder passed in so you can make a second registration
// on the fly like this. Not perfect, but not too much to sacrifice.
builder.RegisterType<CallLogger>().WithParameter("prefix", prefix).Named<IInterceptor>(interceptorId);
return registration;
}
}
}
Looks like the example solved the problem. If not, consider opening another question on StackOverflow and tag it autofac
. We have a lot of folks out there that can help with ideas on additional ways to use the existing extensibility points.
Currently it's not possible to specify parameters to be used when resolving an interceptor service. I recently had a need for this and I thought that it might be useful for others as well.
One scenario would be to supply configuration options to the interceptor.
Usage would be as follows:
or with attributes
I have a PR ready that we can discuss if this is of interest.