Autofac 通用类型注册与拦截器并解决它

Autofac generic type registration with interceptor and resolve it

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

OwnedByLifetimeScope cannot use interface interception as it provides services that are not publicly visible interfaces. Check your registration of the component to ensure you're not enabling interception and registering it as an internal/private interface type.

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

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

编辑: 我在其他 interfaces.It 上使用这个拦截器,在没有 CommandResolver 拦截器的情况下效果很好。当我尝试拦截 ICommandHandler<,> 时,它不起作用。我没有在这里写拦截器注册字符串代码,正如我所说,它适用于其他接口,例如 ICommandBus。没有任何私有或内部接口,我之前已经检查过。

看来您发现了一个错误!我已经 filed an issue with the Autofac.Extras.DynamicProxy repository on your behalf 并且在该期中包含了经过精简的完整重现。您可以在那里订阅以跟进。

我们希望能尽快解决这个问题,但是,如果您有一个带有解决方案的 PR,它可能会更快。

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

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

只要您只通过接口解析,就可以通过修改 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();