Reputation: 23
My application contains many non-generic implementations of the generic ICommandHandler<TRequest, TResponse>
interface. I am trying to add interceptors to them, by calling EnableInterfaceInterceptors
. But when I try to resolve a command handler, Autofac throws an exception with the following message:
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.
This is my registration and resolver code. How can i use interceptors with generic types and resolve it?
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);
}
}
EDIT: i am using this interceptors on other interfaces.It works great without interceptors in CommandResolver. When i try to intercept ICommandHandler<,>, it doesnt work. I didn't write here interceptors registring code, as i said,it works on other interfaces, for example on ICommandBus. there isn't any private or internal interface, i have examined before.
Upvotes: 2
Views: 1263
Reputation: 2167
The issue here is actually the fact that AsClosedTypesOf()
registers a service as both the concrete class and the closed interface - as they are both closed types of the provided interface.
As long as you are only ever resolving via the interface you can solve this issue by modifying the AsClosedTypesOf()
extension method to only register interface types.
/// <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)));
}
And then you can change your registration to
builder.RegisterAssemblyTypes(assemblyType.Assembly)
.AsClosedInterfacesOf(typeof(ICommandHandler<,>))
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(LoggingInterceptor))
.InterceptedBy(typeof(ExceptionHandlingInterceptor))
.InstancePerLifetimeScope();
Upvotes: 2
Reputation: 23924
It appears you've found a bug! I've filed an issue with the Autofac.Extras.DynamicProxy repository on your behalf and I've included a trimmed down complete repro in that issue. You can subscribe there to follow along.
We can hopefully resolve that soon, however, if you have a PR with a resolution it'll likely be much faster.
Sorry that's not the answer you were probably hoping for, but at least it's an answer.
Upvotes: 0