【问题标题】:Using Autofac Interface Interception with IAsyncInterceptor通过 IAsyncInterceptor 使用 Autofac 接口拦截
【发布时间】:2018-08-30 21:56:29
【问题描述】:

我确实使用了这个文档: https://autofaccn.readthedocs.io/en/latest/advanced/interceptors.html

实现接口拦截器。为了处理我的异步调用,我使用了此处描述的 IAsyncInterceptor 接口:

https://github.com/JSkimming/Castle.Core.AsyncInterceptor

我想出的注册码是这样的:

 builder.Register(c => new CallResultLoggerInterceptor())
    .Named<IAsyncInterceptor>("log-calls");

  builder.RegisterType<AppointmentService>()
    .As<IAppointmentService>()
    .EnableInterfaceInterceptors()
    .InstancePerDependency();

AppointmentService 有一个 InterceptAttribute。

 [Intercept("log-calls")]
 public class AppointmentService : IAppointmentService
 ...

当我调用容器的 Build() 方法时,它会抛出一个带有以下消息的 ComponentNotRegisteredException:

请求的服务“log-calls (Castle.DynamicProxy.IInterceptor)”尚未注册。为避免此异常,请注册组件以提供服务,使用 IsRegistered() 检查服务注册,或使用 ResolveOptional() 方法解决可选依赖项。

这是正确的,因为我没有实现 IInterceptor 而是 IAsyncInterceptor。我想问题是使用 ProxyGenerator 的“错误”扩展方法在 autofac 中实现 EnableInterfaceInterceptors 的具体实现 - 但我该如何解决这个问题?

干杯, 曼努埃尔

【问题讨论】:

    标签: autofac castle-dynamicproxy


    【解决方案1】:

    您需要注册一个名为 IInterceptor 的 Autofac 拦截器才能工作。您正在注册IAsyncInterceptor。那是行不通的。

    注意 Autofac 不支持您正在使用的这种扩展的异步拦截器扩展。如果你想让它工作,它需要编写一个具有某种性质的自定义适配器来让它响应IInterceptor。

    【讨论】:

      【解决方案2】:

      您可以在 Castle.Core.AsyncInterceptor 的问题中看到我的回答: https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/42#issuecomment-592074447

      1. 创建适配器
      public class AsyncInterceptorAdaper<TAsyncInterceptor> : AsyncDeterminationInterceptor
        where TAsyncInterceptor : IAsyncInterceptor
      {
          public AsyncInterceptorAdaper(TAsyncInterceptor asyncInterceptor)
              : base(asyncInterceptor)
          { }
      }
      
      1. 创建您的异步拦截器
      public class CallLoggerAsyncInterceptor : AsyncInterceptorBase  
      {
        ....
      }
      
      1. 将拦截器与接口相关联
      [Intercept(typeof(AsyncInterceptorAdaper<CallLoggerAsyncInterceptor>))]
      public interface ISomeType
      
      1. 注册到 IoC 容器
      //register adapter
      builder.RegisterGeneric(typeof(AsyncInterceptorAdaper<>));
      //register async interceptor
      builder.Register(c => new CallLoggerAsyncInterceptor(Console.Out));   
      

      我在https://github.com/wswind/aop-learn/blob/master/AutofacAsyncInterceptor做了一个代码示例

      【讨论】:

        【解决方案3】:

        我创建了自己的扩展方法来注册应用程序服务。 这种扩展方法只是为城堡核心ProxyGenerator准备输入参数。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using Castle.DynamicProxy;
        using Autofac;
        
        namespace pixi.Extensions
        {
            public static class AutofacExtensions
            {
                private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator();
        
                /// <summary>
                /// Use this extension method to register default interceptors <code>UnitOfWorkInterceptor</code>
                /// and <code>LoggingInterceptor</code> on your application service implementations. If you need custom
                /// interceptors that are not part of infrastructure but are part of specific business module then pass
                /// in those interceptors in params explicitly.
                /// </summary>
                /// <param name="builder"></param>
                /// <param name="interceptors"></param>
                /// <typeparam name="TImplementation"></typeparam>
                /// <typeparam name="TService"></typeparam>
                /// <exception cref="ArgumentException"></exception>
                public static void RegisterApplicationService<TImplementation, TService>(this ContainerBuilder builder, params Type[] interceptors)
                    where TImplementation : class
                {
                    ValidateInput<TService>(interceptors);
        
                    builder.RegisterType<TImplementation>().AsSelf();
        
                    builder.Register(c =>
                    {
                        var service = c.Resolve<TImplementation>();
                        var resolvedInterceptors = ResolveInterceptors<TImplementation, TService>(interceptors, c);
        
                        return (TService) _proxyGenerator.CreateInterfaceProxyWithTarget(
                                typeof(TService),
                                service,
                                ProxyGenerationOptions.Default,
                                resolvedInterceptors
                            );
                    }).As<TService>();
                }
        
                private static void ValidateInput<TService>(Type[] interceptors)
                {
                    if (!typeof(TService).IsInterface)
                        throw new ArgumentException("Type must be interface");
                    if (interceptors.Any(i => i != typeof(IAsyncInterceptor)))
                        throw new ArgumentException("Only IAsyncInterceptor types are expected");
                }
        
                private static IAsyncInterceptor[] ResolveInterceptors<TImplementation, TService>(Type[] interceptors,
                    IComponentContext c) where TImplementation : class
                {
                    var resolvedInterceptors = new List<IAsyncInterceptor>
                    {
                        c.Resolve<LoggingInterceptor>(),
                        c.Resolve<UnitOfWorkInterceptor>()
                    }.Concat(interceptors
                        .Where(i => i != typeof(UnitOfWorkInterceptor)
                                    && i != typeof(LoggingInterceptor))
                        .Select(i => (IAsyncInterceptor) c.Resolve(i))).ToArray();
                    return resolvedInterceptors;
                }
            }
        }
        

        我将城堡核心用于工作单元和日志记录,因此名称为 UnitOfWorkInterceptor 和 LogginInterceptor。将这两个更改为您想要的默认值。默认拦截器必须以这种方式注册:

        public class SomeModule: Module
            {
                protected override void Load(ContainerBuilder builder)
                {
                    builder.RegisterType<UnitOfWorkInterceptor>().AsSelf();
                    builder.RegisterType<LoggingInterceptor>().AsSelf();
                    builder.RegisterApplicationService<SampleService, ISampleService>();
                    builder.RegisterType<SampleRepository>().As<ISampleRepository>();
                }
            }
        

        在上面的代码 sn-p 中,我还演示了使用提供的扩展方法。这样做我会得到红色的标签接口并在接口上放置额外的属性。这样我就可以让我的 ApplicationService 接口不受框架/第三方库的依赖。

        我希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-24
          • 2015-04-29
          • 1970-01-01
          • 1970-01-01
          • 2015-07-07
          • 2015-04-16
          相关资源
          最近更新 更多