#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