注册通用类型

时间:2016-09-28 14:43:38

标签: c# generics dependency-injection autofac

我们说我有这堂课:

public class DetailedQueryHandler<T> : IQueryHandlerAsync<Detailed, T> 
    where T : CalculationQuery

我可以这样注册:

builder.RegisterType(typeof(DetailedQueryHandler<CalculationWithDealerQuery>))
       .As(typeof(IQueryHandlerAsync<Detailed, CalculationWithDealerQuery>));
builder.RegisterType(typeof(DetailedQueryHandler<CalculationQuery>))
       .As(typeof(IQueryHandlerAsync<Detailed, CalculationQuery>));

但我想以更自动化的方式注册它,就像我可以在下面的IQueryHandlerAsync接口注册类型:

var types = ThisAssembly.GetTypes();
builder.RegisterTypes(types)
       .Where(t => t.ImplementGenericInterface(t2 => t2 == typeof(IQueryHandlerAsync<,>))
       .AsImplementedInterfaces()

public interface IQueryHandlerAsync<T, in TI>

public static bool ImplementGenericInterface(this Type type, Func<Type, bool> comparer) {
        return type.GetInterfaces().Any(i => i.IsGenericType 
            && comparer(i.GetGenericTypeDefinition()));
}

解决问题的好方法是什么?

1 个答案:

答案 0 :(得分:0)

在理解您的代码时,您需要一种方法将特定的IQueryHandlerAsync实施与特定的CalculationQuery相关联。

执行此操作的一种可能方法是注册所需的类型并创建AsQueryHandler方法为您进行注册:

public static class ContainerBuilderExtensions
{
    public static IRegistrationBuilder<TLimit, TConcreteActivatorData, SingleRegistrationStyle> AsQueryHandler<TLimit, TConcreteActivatorData>(this IRegistrationBuilder<TLimit, TConcreteActivatorData, SingleRegistrationStyle> registration)
        where TConcreteActivatorData : IConcreteActivatorData

    {
        Type queryHandlerType = registration.ActivatorData.Activator.LimitType;
        Type queryHandlerRegistrationType = queryHandlerType.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueryHandlerAsync<,>));
        if (queryHandlerRegistrationType == null)
        {
            throw new ArgumentException($"{queryHandlerType} doesn't implement {typeof(IQueryHandlerAsync<,>).Name} interface");
        }
        TypedService queryHandlerService = new TypedService(queryHandlerRegistrationType);

        return registration.As(queryHandlerService);
    }
}

你会像这样使用它:

builder.RegisterType<DetailedQueryHandler<CalculationQuery>>()
       .AsQueryHandler();

如果您不想手动注册所有类型,则必须根据需要进行装配扫描以根据需要构建所需类型。

另一种考虑方法是实现IRegistrationSource接口。此接口包含RegistrationsFor方法,当 Autofac 需要新组件时,将调用该方法。您可以将此界面视为根据需求动态注册组件的方法。

public class QueryHandlerRegistrationSource : IRegistrationSource
{
    public Boolean IsAdapterForIndividualComponents
    {
        get
        {
            return false;
        }
    }

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        Type serviceType = (service as IServiceWithType)?.ServiceType;

        if (serviceType == null)
        {
            yield break;
        }
        if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IQueryHandlerAsync<,>))
        {
            Type[] argumentTypes = serviceType.GetGenericArguments();
            Type t0 = argumentTypes[0];
            Type t1 = argumentTypes[1];

            if (t0 == typeof(Detailed))
            {
                IComponentRegistration registration = RegistrationBuilder.ForType(typeof(DetailedQueryHandler<>).MakeGenericType(t1))
                                                                         .As(service)
                                                                         .CreateRegistration();
                yield return registration;
            }
            else
            {
                throw new NotSupportedException();
            }
        }

    }
}

不要忘记注册注册来源:

builder.RegisterSource(new QueryHandlerRegistrationSource()); 

您可以使用if (t0 == typeof(Detailed))作为IDictionary<Type, Type>的参数,而不是QueryHandlerRegistrationSource

相关问题