Search code examples
c#.netautofacautofac-configurationautofac-module

Autofac RegisterAssemblyOpenGenericTypes matching query


I'm attempting to register all open generic components I tag with a custom attribute. The query finds the class correctly but the interface is not registered properly. I'm able to register and resolve the component fine using RegisterGeneric

**Working: **


/// <inheritdoc />
[GenericTransient]
public sealed class RetryPolicyService<TResult> : IRetryPolicyService<TResult>
{...
}

builder.RegisterGeneric(typeof(RetryPolicyService<>))
                .As(typeof(IRetryPolicyService<>))
                .InstancePerDependency();

Not Working:

builder.RegisterAssemblyOpenGenericTypes(Assembly.GetExecutingAssembly())
                .Where(t =>
                    t.GetCustomAttribute<GenericTransientAttribute>() != null)
                .As(t => t.GetInterfaces()[0]);

Not Working:

 builder.RegisterAssemblyOpenGenericTypes(Assembly.GetExecutingAssembly())
                .Where(t =>
                    t.GetCustomAttribute<GenericTransientAttribute>() != null)
                .AsImplementedInterfaces();

Break Point enter image description here

The expectation is to be able to inject IRetryPolicyService into objects. Again its working as expected when I register the type specifically.

private readonly IHttpClientService _httpClient;
private readonly IRetryPolicyService<HttpResponseMessage> _retryPolicy;

public ServiceController(
    IHttpClientService httpClient,
    IRetryPolicyService<HttpResponseMessage> retryPolicy)
{
    _httpClient = httpClient;
    _retryPolicy = retryPolicy;
    _retryPolicy.CreatePolicy(5, times => TimeSpan.FromMilliseconds(times * 1000));
}

Solution

  • The following simplified example works fine. If you uncomment GenericTransient attribute on the second service then controller will get its instance (last registration wins). And the retryPolicies variable will get 2 instances.

    The first variant (with GetInterfaces()) also worked after adding .GetGenericTypeDefinition().

    using Autofac;
    using System.Reflection;
    
    namespace Console.Core6
    {
        internal class Program
        {
            static void Main(string[] args)
            {
                var builder = new ContainerBuilder();
    
                builder.RegisterAssemblyOpenGenericTypes(Assembly.GetExecutingAssembly())
                    .Where(t => t.GetCustomAttribute<GenericTransientAttribute>() != null)
                    .AsImplementedInterfaces();
    
                builder.RegisterAssemblyOpenGenericTypes(Assembly.GetExecutingAssembly())
                    .Where(t => t.GetCustomAttribute<GenericTransientAttribute>() != null)
                    .As(t => t.GetInterfaces()[0].GetGenericTypeDefinition());
    
                builder.RegisterType<ServiceController>().InstancePerDependency();
    
                var container = builder.Build();
                var controller = container.Resolve<ServiceController>();
                var retryPolicies = container.Resolve<IEnumerable<IRetryPolicyService<HttpResponseMessage>>>();
            }
        }
    
        internal class GenericTransientAttribute : Attribute
        { }
    
        public interface IRetryPolicyService<TResult>
        { }
    
        [GenericTransient]
        public sealed class RetryPolicyService<TResult> : IRetryPolicyService<TResult>
        {}
    
        //[GenericTransient]
        public sealed class RetryPolicyService2<TResult> : IRetryPolicyService<TResult>
        {}
    
        public class ServiceController
        {
            private readonly IRetryPolicyService<HttpResponseMessage> _retryPolicy;
    
            public ServiceController(IRetryPolicyService<HttpResponseMessage> retryPolicy)
            {
                _retryPolicy = retryPolicy;
            }
        }
    }