简体   繁体   English

使用autofac条件注册open-generic的装饰器

[英]Conditional registering decorator for open-generic with autofac

I have these open-generics: 我有这些开放式仿制药:

public interface IQuery<out TResult> {}

public interface ICacheableQuery<out TResult> : IQuery<TResult> {
    string CacheKey { get; }
} 

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

and this single decorator: 而这个装饰者:

public class CacheableQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : ICacheableQuery<TResult> {

    public TResult Handle(TQuery query) {
        // doing stuffs....
    }
}

What I want is to register decorator only for queries which are implementing ICacheableQuery<out TResult> . 我想要的是只为正在实现ICacheableQuery<out TResult>查询注册装饰器。 I'm registering components like this: 我正在注册这样的组件:

builder.RegisterAssemblyTypes(assemblies)
       .AsClosedTypesOf(typeof (IQueryHandler<,>))
       .AsImplementedInterfaces()
       .Named("queryHandler",typeof(IQueryHandler<,>));

builder.RegisterGenericDecorator(
    typeof(CacheableQueryHandlerDecorator<,>),
    typeof(IQueryHandler<,>),
    fromKey: "queryHandler");

But it registers the decorator for all types. 但它注册所有类型的装饰器。 Any idea? 任何想法? Thanks in advance. 提前致谢。

Unfortunately, Autofac hasn't got this feature out-of-the-box. 不幸的是,Autofac还没有开箱即用的功能。 However, it can be achieved implementing a RegistrationSource: 但是,它可以实现RegistrationSource:

public interface IGenericDecoratorRegistrationBuilder : IHideObjectMembers
{
    IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter = null, Func<Type, IEnumerable<Parameter>> paramsGetter = null); 
}

public static class GenericDecorators
{       
    public class GenericDecoratorRegistration
    {
        public Type Type;
        public Func<Type, bool> Filter;
        public Func<Type, IEnumerable<Parameter>> ParamsGetter;
    }

    class GenericDecoratorRegistrationBuilder : IGenericDecoratorRegistrationBuilder
    {
        readonly List<GenericDecoratorRegistration> decorators = new List<GenericDecoratorRegistration>();

        public IEnumerable<GenericDecoratorRegistration> Decorators
        {
            get { return decorators; }
        }

        public IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter, Func<Type, IEnumerable<Parameter>> paramsGetter)
        {
            if (decoratorType == null)
                throw new ArgumentNullException("decoratorType");

            if (!decoratorType.IsGenericTypeDefinition)
                throw new ArgumentException(null, "decoratorType");

            var decorator = new GenericDecoratorRegistration
            {
                Type = decoratorType,
                Filter = filter,
                ParamsGetter = paramsGetter
            };

            decorators.Add(decorator);

            return this;
        }
    }

    class GenericDecoratorRegistrationSource : IRegistrationSource
    {
        readonly Type decoratedType;
        readonly IEnumerable<GenericDecoratorRegistration> decorators;
        readonly object fromKey;
        readonly object toKey;

        public GenericDecoratorRegistrationSource(Type decoratedType, IEnumerable<GenericDecoratorRegistration> decorators, object fromKey, object toKey)
        {
            this.decoratedType = decoratedType;
            this.decorators = decorators;
            this.fromKey = fromKey;
            this.toKey = toKey;
        }

        public bool IsAdapterForIndividualComponents
        {
            get { return true; }
        }

        public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
        {
            var swt = service as IServiceWithType;
            KeyedService ks;
            if (swt == null ||
                (ks = new KeyedService(fromKey, swt.ServiceType)) == service ||
                !swt.ServiceType.IsGenericType || swt.ServiceType.GetGenericTypeDefinition() != decoratedType)
                return Enumerable.Empty<IComponentRegistration>();

            return registrationAccessor(ks).Select(cr => new ComponentRegistration(
                    Guid.NewGuid(),
                    BuildActivator(cr, swt),
                    cr.Lifetime,
                    cr.Sharing,
                    cr.Ownership,
                    new[] { toKey != null ? (Service)new KeyedService(toKey, swt.ServiceType) : new TypedService(swt.ServiceType) },
                    cr.Metadata,
                    cr));
        }

        DelegateActivator BuildActivator(IComponentRegistration cr, IServiceWithType swt)
        {
            var limitType = cr.Activator.LimitType;
            var actualDecorators = decorators
                .Where(d => d.Filter != null ? d.Filter(limitType) : true)
                .Select(d => new { Type = d.Type, Parameters = d.ParamsGetter != null ? d.ParamsGetter(limitType) : Enumerable.Empty<Parameter>() })
                .ToArray();

            return new DelegateActivator(cr.Activator.LimitType, (ctx, p) =>
            {
                var typeArgs = swt.ServiceType.GetGenericArguments();
                var service = ctx.ResolveKeyed(fromKey, swt.ServiceType);

                foreach (var decorator in actualDecorators)
                {
                    var decoratorType = decorator.Type.MakeGenericType(typeArgs);
                    var @params = decorator.Parameters.Concat(new[] { new TypedParameter(swt.ServiceType, service) });
                    var activator = new ReflectionActivator(decoratorType, new DefaultConstructorFinder(), new MostParametersConstructorSelector(),
                        @params, Enumerable.Empty<Parameter>());
                    service = activator.ActivateInstance(ctx, @params);
                }

                return service;
            });
        }
    }

    public static IGenericDecoratorRegistrationBuilder RegisterGenericDecorators(this ContainerBuilder builder, Type decoratedServiceType, object fromKey, object toKey = null)
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        if (decoratedServiceType == null)
            throw new ArgumentNullException("decoratedServiceType");

        if (!decoratedServiceType.IsGenericTypeDefinition)
            throw new ArgumentException(null, "decoratedServiceType");

        var rb = new GenericDecoratorRegistrationBuilder();
        builder.RegisterCallback(cr => cr.AddRegistrationSource(new GenericDecoratorRegistrationSource(decoratedServiceType, rb.Decorators, fromKey, toKey)));

        return rb;
    }
}

Sample usage: 样品用法:

public interface IGeneric<T>
{
    void SomeMethod();
}

class IntImpl : IGeneric<int>
{
    public void SomeMethod() { }
}

class StringImpl : IGeneric<string>
{
    public void SomeMethod() { }
}

class GenericDecorator<T> : IGeneric<T>
{
    IGeneric<T> target;

    public GenericDecorator(IGeneric<T> target)
    {
        this.target = target;
    }

    public void SomeMethod()
    {
        target.SomeMethod();
    }
}

static void Configure(ContainerBuilder builder)
{
    builder.RegisterType<IntImpl>().Named<IGeneric<int>>("generic");
    builder.RegisterType<StringImpl>().Named<IGeneric<string>>("generic");

    builder.RegisterGenericDecorators(typeof(IGeneric<>), "generic")
        // applying decorator to IGeneric<string> only
        .Decorator(typeof(GenericDecorator<>), t => typeof(IGeneric<string>).IsAssignableFrom(t));
    }

Please note 请注意

  • You must key the registrations of decorated components because (as far as I know) there's no way to override these with dynamic registrations provided by the RegistrationSource. 您必须键入装饰组件的注册,因为(据我所知),无法通过RegistrationSource提供的动态注册覆盖这些注册。

  • In this solution the decorated component inherits the configuration of the decorated one (scoping, sharing, ownership, etc) 在此解决方案中,装饰组件继承了装饰组件的配置(范围,共享,所有权等)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM