4

複雑な要件があると思われる Autofac モジュールをセットアップしようとしています。

ここに行きます:

私は一般的なインターフェースを持っています:

public interface IMyInterface<TFoo, TBar>

このインターフェースを実装するクラスがたくさんあります

例えば

class MyImpl1 : IMyInterface<string, bool> { }
class MyImpl2 : IMyInterface<bool, string> { }
class MyImpl3 : IMyInterface<bool, string> { }

最後に、デコレータがあります。

class MyDecorator<TFoo, TBar> : IMyInterface<TFoo, TBar>

MyInterface特定の属性を持つ(の) 実装を「装飾」したいだけです。したがって、属性を持つ MyInterface のすべての実装は、MyDecorator で[MyAttribute] 装飾されます。

私は近くにいますが、葉巻はまだありません:

var builder = new ContainerBuilder();        

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
    .Where(type => type.GetCustomAttributes(true)
        .Any(attr => attr.GetType() == typeof(MyAttribute)))
    .AsClosedTypesOf(typeof (IMyInterface<,>))
    .Keyed("CachableQueries", typeof(IMyInterface<,>));

builder.RegisterGenericDecorator(typeof(MyDecorator<,>),
    typeof(IMyInterface<,>), "CachableQueries");

var container = builder.Build();

Console.WriteLine(container.Resolve<IMyInterface<string,bool>>());
Console.WriteLine(container.Resolve<IMyInterface<bool,bool>>());

パズルの最後のピースがキーであることを理解しています。実際には、型を に渡す必要がありますKeyed("CachableQueries", THE_TYPE);が、ボールを再生していません。

アップデート

nemesvは私を正しい方向に送りました。

私の質問の一部として、[MyAttribute] を持たない IMyInterface<,> のすべての実装も登録する必要があることを忘れていました。

これを2段階で行いました。最初にデコレーターに型を登録してから、残りを登録します。

私の解決策: リファクタリングが必要なのはわかっていますが、概念実証としてです。できます。

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();

        //Get all the types we're interested in (that inherit IMyInterface)
        List<Type> typesToQuery = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => type.GetInterfaces()
                    .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IMyInterface<,>))).ToList();

        //Even tho the decorator inherits IMyInterface (we don't want to process it)
        typesToQuery.Remove(typeof (MyDecorator<,>)); 


        //build a dictionary of all the types, so we don't process them again.
        Dictionary<Type, bool> typesToProcess = typesToQuery.ToDictionary(queryType => queryType, queryType => false);


        //Register all types that have [MyAttribute]
        foreach (var type in typesToQuery
                    .Where(type => type.GetCustomAttributes(true)
                    .Any(attr => attr.GetType() == (typeof(MyAttribute)))))
        {
            builder.RegisterType(type).Keyed("CachableQueries",
                type.GetInterfaces()
                    .First(i =>
                            i.IsGenericType &&
                            i.GetGenericTypeDefinition() == typeof(IMyInterface<,>)));

            typesToProcess[type] = true; //update, so this type isn't processed again
        }

        //Decorate the correct ones
        builder.RegisterGenericDecorator(typeof(MyDecorator<,>), typeof(IMyInterface<,>), fromKey: "CachableQueries");

        //Register the rest of the types we're interested 
        foreach (Type type in typesToProcess.Where(kvp => kvp.Value == false).Select(pair => pair.Key))
        {
            builder.RegisterType(type).As(
                type.GetInterfaces()
                    .First(i =>
                            i.IsGenericType &&
                            i.GetGenericTypeDefinition() == typeof(IMyInterface<,>)));

        } 

        var container = builder.Build();

        Console.WriteLine(container.Resolve<IMyInterface<string, bool>>());
        Console.WriteLine(container.Resolve<IMyInterface<bool, bool>>());

        //Result:
        //AutoFacPlay.MyDecorator`2[System.String,System.Boolean]  - this one was decorated (as it has MyAttribute)
        //AutoFacPlay.MyImplementation2 - this one wasn't decorated

        Console.ReadLine();

    }
}
4

2 に答える 2

2

わかりました。ちょうど 1 週間前に更新されたので、質問が 3 年前のものであることに気付きませんでした。

登録時に連鎖メソッドを利用して、属性で装飾された型をそうでない型から分離できます。

using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;

namespace ConsoleApplication1
{
    public interface IOpenGeneric<T, U>
    {
        U Get(T value);
    }

    [AttributeUsage(AttributeTargets.Class)]
    public class DecorateAttribute : Attribute
    {
    }

    [Decorate]
    public class BooleanToStringOne : IOpenGeneric<bool, string>
    {
        public string Get(bool value)
        {
            return $"{value.ToString()} from BooleanToStringOne";
        }
    }

    [Decorate]
    public class BooleanToStringTwo : IOpenGeneric<bool, string>
    {
        public string Get(bool value)
        {
            return $"{value.ToString()} from BooleanToStringTwo";
        }
    }

    public class BooleanToStringThree : IOpenGeneric<bool, string>
    {
        public string Get(bool value)
        {
            return $"{value.ToString()} from BooleanToStringThree";
        }
    }

    public class OpenGenericDecorator<T, U> : IOpenGeneric<T, U>
    {
        private readonly IOpenGeneric<T, U> _inner;

        public OpenGenericDecorator(IOpenGeneric<T, U> inner)
        {
            _inner = inner;
        }

        public U Get(T value)
        {
            Console.WriteLine($"{_inner.GetType().Name} is being decorated!");
            return _inner.Get(value);
        }
    }

    public static class ReflectionExtensions
    {
        public static bool HasAttribute<TAttribute>(this Type type)
            where TAttribute : Attribute
        {
            return type
                .GetCustomAttributes(typeof(TAttribute), false)
                .Cast<Attribute>()
                .Any();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var assembly = typeof(Program).Assembly;
            var builder = new ContainerBuilder();

            // associate types that have the [Decorate] attribute with a specific key
            builder
                .RegisterAssemblyTypes(assembly)
                .Where(x => x.HasAttribute<DecorateAttribute>())
                .AsClosedTypesOf(typeof(IOpenGeneric<,>), "decoratable-service");

            // get the keyed types and register the decorator
            builder.RegisterGenericDecorator(
                typeof(OpenGenericDecorator<,>),
                typeof(IOpenGeneric<,>),
                "decoratable-service");

            // no key for the ones with no [Decorate] attribute so they'll
            // get resolved "as is"
            builder
                .RegisterAssemblyTypes(assembly)
                .Where(x => !x.HasAttribute<DecorateAttribute>())
                .AsClosedTypesOf(typeof(IOpenGeneric<,>));

            var container = builder.Build();

            var booleanToStrings = container.Resolve<IEnumerable<IOpenGeneric<bool,string>>>();
            foreach (var item in booleanToStrings)
            {
                Console.WriteLine(item.Get(true));
                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}

コンソールからの出力は

BooleanToStringTwo is being decorated!
True from BooleanToStringTwo

BooleanToStringOne is being decorated!
True from BooleanToStringOne

True from BooleanToStringThree
于 2016-12-13T05:05:15.997 に答える