Autofac: как использовать внедрение конструкции в класс с зависимостью от вывода другой объектной функции

#c# #dependency-injection #inversion-of-control #autofac

#c# #внедрение зависимости #инверсия управления #autofac

Вопрос:

Этот вопрос касается IoC в целом, но я использую Autofac, поэтому решение Autofac было бы отличным.

Итак, предположим, что следующие классы:

 class A
{
    IEnumerable<B> GetBs();
}

class B 
{
    // Some code
}

class C
{
    readonly IEnumerable<B> bs;

    C(IEnumerable<B> bs)
    {
        this.bs = bs;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.Register<A>();
        builder.Register<C>();

        var container = builder.Build();
        var c = container.Resolve<C>();
        // do something with c
    }
}
  

вышеописанное Main завершится неудачей.

Я заметил, что могу решить эту проблему, добавив эту строку кода:

 builder.Register(c => c.Resolve<A>().GetBs())
  

Однако это кажется неправильным. Есть ли другой способ добиться этого? Или изменения дизайна?

Ответ №1:

Как указано, эта структура класса выглядит идеально. Вы правильно используете внедрение конструктора и используете статическую структуру классов для передачи инвариантов и отношений. На уровне представленной здесь информации, я думаю, единственное, чего не хватает, это защитного предложения в конструкторе C 🙂

На концептуальном уровне сложнее ответить, хороший это дизайн или нет. Это зависит от контекста, который здесь не указан. Является ли B сервисом или сущностью? Каков предполагаемый источник Bs?

Как общее эмпирическое правило, мы должны внедрять только сервисы, но обрабатывать объекты с помощью сервисов (таких как репозитории или шлюзы или что-то еще), поэтому, если B представляет собой сервис, было бы нормально внедрить его в C. Если он представляет сущность, то это становится подозрительным — если C также не является сущностью…

В другом примечании вы можете спросить о каноническом источнике Bs. Если A действительно предназначено быть контейнером Bs, то приведенное выше решение правильное (и идиоматический код Autofac). Если Bs действительно имеют другое происхождение, их разрешение с помощью A может быть взломом…

Комментарии:

1. По соображениям простоты я назвал классы A, B и C. Но вы правы, говоря, что без контекста трудно определить, является ли дизайн концептуально правильным.

2. Класс A представляет собой менеджер плагинов. Фактически выполняемая функция get на самом деле является универсальной и возвращает все плагины, которые реализуют конкретный плагин. Класс B представляет собой реализацию плагина. Класс C является потребителем, который имеет дело только с определенным типом плагина. Спасибо за отметку о быстром ответе.

Ответ №2:

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

Комментарии:

1. Хорошее предложение — вам больше не нужно использовать API RegisterCollection в версии 2, хотя — IEnumerable<Что угодно> может быть разрешено по умолчанию. Я устарею или удалю страницу wiki, спасибо за указатель.

2. Я думал, что коллекции были разрешены автоматически в текущей версии, но я по глупости воспользовался документацией 😉

3. Спасибо, ребята. Я использую RegisterCollection<T>().Как<IEnumerable<T>>() в PluginManager. Я не могу использовать v2, потому что система плагинов предназначена для устаревшей программы, которая работает на .Net 2.0