【问题标题】:Autofac global interface interceptor with Autofac.Extras.DynamicProxy2Autofac 全局接口拦截器与 Autofac.Extras.DynamicProxy2
【发布时间】:2014-04-01 09:42:26
【问题描述】:

我正在使用 Autofac DynamicProxy2 的接口拦截器,并且我能够为每个寄存器启用接口拦截器:

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

我想将某些特征应用于所有已注册的类型。比如:

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

我找不到获取所有注册的方法。我知道我们可以做到:

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

但是这里的注册是IComponentRegistration,我需要IRegistrationBuilder来申请EnableInterfaceInterceptors()

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    您可以动态添加拦截器,但需要做一些工作。要走的路是创建一个自定义的Autofac.Module 附加到所有组件注册。我将通过示例向您展示。

    你不能真正在全局范围内执行EnableInterfaceInterceptors我将在示例结束时进行说明。

    首先,示例设置:我们有一个简单的接口、一个简单的实现和一个用于处理日志调用的拦截器。 (我在窃取拦截器代码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);
      }
    }
    

    我们希望通过调用记录器拦截所有内容(启用了拦截器)。我们通过创建自定义 Autofac.Module 来实现,该 Autofac.Module 将拦截器本身注册到 Autofac 并附加到组件注册快速添加拦截器元数据。

    警告:这里有一点黑客攻击,它可以工作,但有点“将数据”“插入”到某种已知的位置。它有效,我不知道它为什么会改变,但请注意,因为它有点像是在处理“私人”的东西,这可能会在未来的版本中中断。请注意。

    好的,免责声明完成。这是模块:

    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's 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 调用... 执行EnableInterfaceInterceptorsEnableClassInterceptors 实际上在后端做了很多疯狂的DynamicProxy2 工作。它向组件上的激活事件添加了一些重要的事件处理程序,将对象包装在动态代理中。这些事件处理程序目前没有公开用于单独使用,我不确定要像我们在此处使用实际拦截器所做的那样尝试“事后”附加所有这些东西需要做多少工作。

    欢迎您亲自尝试 - the source is on GitHub。但是,基本上,虽然“添加全局拦截器”的工作有效,但在模块中执行全局 EnableInterfaceInterceptors 却远非人迹罕至。你肯定会独自一人。

    【讨论】:

    • 感谢您的回答。但是我也想从寄存器中省略 EnableInterfaceInterceptor 。我更愿意将这两个问题分开 - 我如何解决它,是否需要拦截它,以及需要什么拦截器。
    • 我更新了我的答案以解决这部分问题。基本上,没有简单的方法可以像附加实际拦截器那样移动 EnableInterfaceInterceptors 调用。
    • 你是对的,之后很难复制 EnableInterfaceInterceptors。我最终得到了一个不同的解决方案,我很快就会发布。
    • 你的解决方案对接口拦截器很有帮助,但它对类拦截器也有用吗?我不能让它工作,但它可能是一些不同的问题......
    • 我没有在类拦截器上尝试过,但如果你弄明白了,一定要回帖。
    【解决方案2】:

    显然,在我们执行注册时放弃EnableInterfaceInterceptors 调用并非易事。

    由于我的目标是实现一个对通过 Autofac 解析的所有接口通用的拦截器,我最终推出了自己的解决方案,灵感来自 Autofac 的 DynamicProxy 实现。

    这个想法是覆盖 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[];
        }
    }
    

    【讨论】:

    • FieldInfo eventHandlerField = registration.GetType().GetField("Activated", BindingFlags.NonPublic | BindingFlags.Instance);无法返回任何东西并且出现错误
    【解决方案3】:

    通过利用构建器模式,可以很容易地将拦截器添加到所有构建器中。像这​​样:

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

    【讨论】:

      猜你喜欢
      • 2018-08-30
      • 1970-01-01
      • 2020-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多