使用拦截器进行Autofac泛型类型注册并解决它

时间:2018-03-30 13:33:54

标签: c# generics dependency-injection autofac cqrs

我的应用程序包含许多通用ICommandHandler<TRequest, TResponse>接口的非泛型实现。我试图通过调用EnableInterfaceInterceptors向他们添加拦截器。但是当我尝试解析命令处理程序时,Autofac会抛出一个异常,并显示以下消息:

  

OwnedByLifetimeScope无法使用接口拦截,因为它提供的服务不是公开可见的接口。检查组件的注册,以确保您没有启用拦截并将其注册为内部/专用接口类型。

这是我的注册和解析器代码。我如何使用泛型类型的拦截器并解决它?

   
builder.RegisterAssemblyTypes(assemblyType.Assembly)
    .AsClosedTypesOf(typeof(ICommandHandler<,>))
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(LoggingInterceptor))
    .InterceptedBy(typeof(ExceptionHandlingInterceptor))
    .InstancePerLifetimeScope();

This is resolver
public class CommandResolver : ICommandBus
{
    private readonly ILifetimeScope _lifetimeScope;

    public CommandResolver(ILifetimeScope lifetimeScope)
    {
        _lifetimeScope = lifetimeScope;
    } 

    public TResult Execute<TResult>(ICommand<TResult> command)
    { 
        var generic = typeof(ICommandHandler<,>);
        var genericArgumentList = new Type[]
        {
            command.GetType(), typeof(TResult)
        };

        var commandHandlerType = generic.MakeGenericType(genericArgumentList);

        // Exception is thrown here
        var handler = (ICommandHandler)_lifetimeScope.Resolve(commandHandlerType);

        return (TResult)handler.Execute(command);  
    }  
} 

修改 我在其他接口上使用这个拦截器。在CommandResolver中没有拦截器的情况下工作得很好。当我试图拦截ICommandHandler&lt;,&gt;时,它不起作用。我没有在这里写拦截器registring代码,正如我所说,它适用于其他接口,例如在ICommandBus上。没有任何私人或内部接口,我之前已经检查过。

2 个答案:

答案 0 :(得分:1)

实际上,这里的问题是AsClosedTypesOf()将服务注册为具体类和封闭接口-因为它们都是所提供接口的封闭类型。

只要您仅通过接口进行解析,就可以通过将扩展名AsClosedTypesOf()修改为仅注册接口类型来解决此问题。

        /// <summary>
        /// Specifies that a type from a scanned assembly is registered if it implements an interface
        /// that closes the provided open generic interface type.
        /// </summary>
        /// <typeparam name="TLimit">Registration limit type.</typeparam>
        /// <typeparam name="TScanningActivatorData">Activator data type.</typeparam>
        /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
        /// <param name="registration">Registration to set service mapping on.</param>
        /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
        /// <returns>Registration builder allowing the registration to be configured.</returns>
        public static IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> AsClosedInterfacesOf<TLimit, TScanningActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration, Type openGenericServiceType) where TScanningActivatorData : ScanningActivatorData
        {
            if ((object)openGenericServiceType == null)
                throw new ArgumentNullException(nameof(openGenericServiceType));
            if (!openGenericServiceType.IsInterface)
                throw new ArgumentException("Generic type must be an interface", nameof(openGenericServiceType));

            return registration
                .Where(candidateType => candidateType.IsClosedTypeOf(openGenericServiceType))
                .As(candidateType =>
                    candidateType.GetInterfaces()
                        .Where(i => i.IsClosedTypeOf(openGenericServiceType))
                        .Select(t => (Service)new TypedService(t)));
        }

然后您可以将注册更改为

builder.RegisterAssemblyTypes(assemblyType.Assembly)
    .AsClosedInterfacesOf(typeof(ICommandHandler<,>))
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(LoggingInterceptor))
    .InterceptedBy(typeof(ExceptionHandlingInterceptor))
    .InstancePerLifetimeScope();

答案 1 :(得分:0)

看来你发现了一个错误!我filed an issue with the Autofac.Extras.DynamicProxy repository on your behalf我已经在这个问题中加入了一个完整的复制品。你可以在那里订阅。

我们可以很快解决这个问题,但是,如果你的PR有分辨率,它可能会更快。

很抱歉,这不是你可能希望的答案,但至少这是一个答案。