使用IAsyncInterceptor

时间:2018-03-21 16:46:27

标签: autofac castle-dynamicproxy

我确实使用过这个文档: 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 - 但我该如何解决这个问题?

干杯, 曼努埃尔

3 个答案:

答案 0 :(得分:1)

您需要为Autofac拦截器注册一个名为IInterceptor的工作。您正在注册IAsyncInterceptor。那不会起作用。

注意Autofac不支持您正在使用的此扩展异步拦截器扩展。如果您希望将其付诸实践,则需要编写某种性质的自定义适配器,以使其响应IInterceptor

答案 1 :(得分:1)

您可以在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中制作了代码示例

答案 2 :(得分:0)

我已经创建了自己的扩展方法来注册应用程序服务。 这种扩展方法只是为城堡核心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;
        }
    }
}

我将城堡核心用于工作单元并记录日志,因此命名为UnitOfWorkInterceptorLogginInterceptor。将这两个更改为所需的默认值。默认拦截器必须以这种方式注册:

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

在上面的代码片段中,我还演示了用法提供的扩展方法。通过这种方式,我获得了标记接口的红色,并在接口上放置了额外的属性。这样,我可以使ApplicationService接口不受框架/第三方库依赖项的影响。

我希望这会有所帮助。