Зарегистрируйте декоратор в autofac без указания всех зависимостей вручную

У меня есть декоратор, который имеет некоторые другие зависимости, которые также должны быть разрешены с помощью контейнера. Пример:

public class FooDecorator : IFoo
{
    public FooDecorator(IFoo inner, IBar bar, IBaz baz)
}

Я могу зарегистрировать это следующим образом:

builder.RegisterType<Foo>().As<IFoo>();
builder.RegisterDecorator<IFoo>((c, inner) => 
    new FooDecorator(inner, c.Resolve<IBar>(), c.Resolve<IBaz>()), "key");

Это работает, однако не так приятно, что мне нужно вручную указать все другие зависимости. Я хотел бы сделать следующее:

builder.RegisterDecorator<FooDecorator, IFoo>("key");

Если IFoo разрешено "внутреннее" IFoo, а остальные зависимости разрешены из контейнера. Возможно ли это, или я могу зарегистрировать декоратор с помощью Func, который приведет к такому поведению?

Ответ 1

Чтобы избежать указания всех зависимостей вручную, вы должны зарегистрировать декоратор в Autofac и разрешить его внутри первого параметра метода RegisterDecorator.

builder.RegisterType<Foo>()
       .Named<IFoo>("original");
builder.RegisterType<FooDecorator>()
       .Named<IFoo>("decorator");
builder.RegisterDecorator<IFoo>((c, inner) => c.ResolveNamed<IFoo>("decorator", TypedParameter.From(inner)), "original")
       .As<IFoo>();

Регистрация декоратора с использованием именованной регистрации позволит избежать конфликтов.

Ответ 2

На основании ответа Кирилла Дюрана, вот общий вспомогательный метод регистрации декоратора:

    private void RegisterDecorator<TInterface, TImplementation, TDecorator>(ContainerBuilder builder) where TImplementation : TInterface where TDecorator : TInterface
    {
        builder.RegisterType<TImplementation>().Named<TInterface>("implementation");
        builder.RegisterType<TDecorator>().Named<TInterface>("decorator");
        builder.RegisterDecorator<TInterface>((c, inner) => c.ResolveNamed<TInterface>("decorator", TypedParameter.From(inner)), "implementation");
    }

Если требуется более одного вложенного декоратора, можно ввести аналогичный метод с более универсальными параметрами (TDecorator1, TDecorator2 и т.д.).

Ответ 3

Основываясь на ответе Эзолотко, я сделал расширение.

public static void RegisterDecorator<TInterface, TImplementation, TDecorator>
    (this ContainerBuilder builder) 
    where TImplementation : TInterface 
    where TDecorator : TInterface
    {
        var innerImplementationName = $"{typeof(TImplementation).Name} as implementation";
        var decoratorName = $"{typeof(TDecorator).Name} as decorator";

        builder.RegisterType<TImplementation>().Named<TInterface>(innerImplementationName);
        builder.RegisterType<TDecorator>().Named<TInterface>(decoratorName);
        builder.RegisterDecorator<TInterface>((c, inner) => c.ResolveNamed<TInterface>(decoratorName, TypedParameter.From(inner)), innerImplementationName);
    }
}

Кретин во мне глубоко ценит то, как аккуратно мы украшаем друг друга ответы на этот вопрос о декораторах.

редактировать

Autofac v4.9 добавляет функцию RegisterDecorator. Проверьте это, это круто.