Autofac Global Interface Interceptor with Autofac.Extras.DynamicProxy2

I use interface interceptors with Autofac DynamicProxy2, and I can enable interface interceptors for each register:

   var registration = builder.RegisterType<AType>().As<AInterface>();
   registration.EnableInterfaceInterceptors().InterceptedBy<AInterceptor>()

I would like to apply a certain trait to all registered types. Sort of:

   var registrations = builder.GetAllRegistrations(); // ops, this does not exist...
   foreach (var registration in registrations) {
       registration.EnableInterfaceInterceptors().InterceptedBy<AInterceptor>()
   }

I cannot find a way to get all registrations. I know what we can do:

   builder.RegisterCallback(cr =>
   {
       foreach (var registration in cr.Registrations)
       {
            // registration is IComponentRegistration
       }
   });

But registration is here IComponentRegistration, and I need to IRegistrationBuilderapply EnableInterfaceInterceptors().

+4
source share
3 answers

Apparently, it is not at all trivial to refuse a call EnableInterfaceInterceptorsat the time the registry is executed.

, , , Autofac, , DynamicProxy Autofac.

, Autofac Activating DynamicProxy . , , .

public static class AutofacExtensions
{
    // DynamicProxy2 generator for creating proxies
    private static readonly ProxyGenerator generator = new ProxyGenerator();

    /// <summary>
    /// Intercept ALL registered interfaces with provided interceptors.
    /// Override Autofac activation with a Interface Proxy.
    /// Does not intercept classes, only interface bindings.
    /// </summary>
    /// <param name="builder">Contained builder to apply interceptions to.</param>
    /// <param name="interceptors">List of interceptors to apply.</param>
    public static void InterceptInterfacesBy(this ContainerBuilder builder, params IInterceptor[] interceptors)
    {
        builder.RegisterCallback((componentRegistry) =>
        {
            foreach (var registration in componentRegistry.Registrations)
            {
                InterceptRegistration(registration, interceptors);
            }
        });
    }

    /// <summary>
    /// Intercept a specific component registrations.
    /// </summary>
    /// <param name="registration">Component registration</param>
    /// <param name="interceptors">List of interceptors to apply.</param>
    private static void InterceptRegistration(IComponentRegistration registration, params IInterceptor[] interceptors)
    {
        // proxy does not get allong well with Activated event and registrations with Activated events cannot be proxied.
        // They are casted to LimitedType in the IRegistrationBuilder OnActivated method. This is the offending Autofac code:
        // 
        // public IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActivated(Action<IActivatedEventArgs<TLimit>> handler)
        // {
        //    if (handler == null) throw new ArgumentNullException("handler");
        //    RegistrationData.ActivatedHandlers.Add(
        //        (s, e) => handler(new ActivatedEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance)));
        //    return this;
        // }
        Delegate[] handlers = GetActivatedEventHandlers(registration);
        if (handlers.Any(h => handlers[0].Method.DeclaringType.Namespace.StartsWith("Autofac")))
        {
            return;
        }

        registration.Activating += (sender, e) =>
        {
            Type type = e.Instance.GetType();

            if (e.Component.Services.OfType<IServiceWithType>().Any(swt => !swt.ServiceType.IsInterface || !swt.ServiceType.IsVisible) || 
                // prevent proxying the proxy 
                type.Namespace == "Castle.Proxies")
            {
                return;
            }

            var proxiedInterfaces = type.GetInterfaces().Where(i => i.IsVisible).ToArray();

            if (!proxiedInterfaces.Any())
            {
                return;
            }

            // intercept with all interceptors
            var theInterface = proxiedInterfaces.First();
            var interfaces = proxiedInterfaces.Skip(1).ToArray();

            e.Instance = generator.CreateInterfaceProxyWithTarget(theInterface, interfaces, e.Instance, interceptors);
        };
    }

    /// <summary>
    /// Get Activated event handlers for a registrations
    /// </summary>
    /// <param name="registration">Registration to retrieve events from</param>
    /// <returns>Array of delegates in the event handler</returns>
    private static Delegate[] GetActivatedEventHandlers(IComponentRegistration registration)
    {
        FieldInfo eventHandlerField = registration.GetType().GetField("Activated", BindingFlags.NonPublic | BindingFlags.Instance);
        var registrations = eventHandlerField.GetValue(registration);
        System.Diagnostics.Debug.WriteLine(registrations);
        return registrations.GetType().GetMethod("GetInvocationList").Invoke(registrations, null) as Delegate[];
    }
}
+5

, .. Autofac.Module, . .

EnableInterfaceInterceptors . .

. , , . ( -):

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

( ) . , Autofac.Module, Autofac , .

: , " " . , , , , , sorta-kinda "-", . .

, . :

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

, . :   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 the magic module:
builder.RegisterModule<InterceptorModule>();
var container = builder.Build();

...

var impl = container.Resolve<IInterface>();
impl.DoWork();

, , :

Calling method DoWork with parameters ... 
Implementation doing work.
Done: result was .

( , /void, !)

EnableInterfaceInterceptors... EnableInterfaceInterceptors EnableClassInterceptors DynamicProxy2 . , . , , " ", .

- GitHub. , , " " , EnableInterfaceInterceptors . .

+7

, . :

var interceptedBuilders = new List<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>();
ContainerBuilder builder = new ContainerBuilder();

interceptedBuilders.Add(builder.RegisterType<First>().As<IFirst>());
interceptedBuilders.Add(builder.RegisterType<Second>().As<ISecond>());

foreach (var x in interceptedBuilders)
{              x.EnableInterfaceInterceptors().InterceptedBy(typeof(AInterceptor));
}
0
source

Source: https://habr.com/ru/post/1534401/


All Articles