Как указать внешние зависимости, необходимые модулю autofac

#autofac

#autofac

Вопрос:

Мы используем модули autofac (обычно на уровне сборки) для регистрации типов, содержащихся в данной сборке. Довольно часто сборка имеет некоторую зависимость от кода (интерфейсов), который предоставляется другими сборками. Эти «внешние» зависимости регистрируются другими модулями других сборок, так что все в порядке.

Проблема в том, что совсем не очевидно, какие внешние зависимости есть у модуля / сборки, поэтому любой, кто использует этот модуль, должен сканировать код на наличие типов, которые требуются, но не зарегистрированы модулем.
Мы «только» (некоторые исключения, но здесь не уместны) используем лямбда-регистрации, поэтому, по крайней мере, нам нужно только посмотреть на модули autofac, но все же я ищу способ объявить зависимости, которые должны быть предоставлены любым, кто использует модуль.

В конце концов, проблема не относится конкретно к autofac, но мне было интересно, есть ли у моих коллег-пользователей autofac какие-либо рекомендации или советы по объявлению внешних зависимостей и обеспечению их предоставления?

Ответ №1:

Для этого нет средства, которое Autofac предоставляет из коробки, и ничего не запланировано.

Вы могли бы сделать что-то пользовательское и создать атрибут для разметки модуля, но это было бы нестандартно для вашей реализации.

 [RequiresService(typeof(IEmailSender))]
[RequiresService(typeof(IOrderHandler))]
public class MyModule : Module
{
  // ...
}
  

Но полномасштабная функция, подобная этой, имеет некоторые логические проблемы и неписаные дополнительные цели, поэтому наличие чего-то автоматизированного для ее обработки, скорее всего, не то, что Autofac когда-либо предоставит.

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

Во-вторых, хотя вы используете только лямбды (которые, я предполагаю, имеют new для каждой встроенной вещи), это на самом деле не самый распространенный шаблон использования. Это означало бы, что человек, изменяющий базовые классы компонентов, должен был бы знать, в каком модуле (ах) регистрируется компонент, и изменять типы. Это связывает базовые компоненты с модулем, выполняющим регистрацию DI.

В-третьих, это также связано с лямбдами, это совершенно не будет работать при сканировании сборки или массовой регистрации открытых дженериков. Обобщения в целом были бы здесь большой проблемой, потому что у вас мог бы быть общий аргумент конструктора, поэтому ваша система атрибутов должна была бы учитывать это вместе (возможно) с общими ограничениями.

В-четвертых, неясно, как можно выразить потребность в сервисе, если он потребляется через одно из встроенных отношений, например, IEnumerable<IService> а не просто IService . Опять же, меньше проблем с очень специфичными лямбда-регистрациями, разваливается при использовании отражения или сканирования или чего-либо еще.

Наконец, неизменно кто-нибудь спросит способ сказать: «учитывая этот набор модулей, все ли зависимости удовлетворены?» Это означает, что должен быть не только какой-то способ опроса того, что требуется, но и того, что предоставляет модуль, что, опять же, сталкивается со всеми вышеупомянутыми проблемами, связанными с выяснением того, как поддерживать соответствие атрибутов тому, что находится в модуле, обработке сканирования, обобщений и т.д.

В любом случае, это действительно длинное объяснение того, почему его нет из коробки. Извините за бессвязность.

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

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

1. Большое спасибо за подробное объяснение.

2. Я знаю о множестве проблем, с которыми сталкивается техническое решение этой проблемы, поэтому я понимаю, что для этого нет встроенного механизма. Для нашего варианта использования атрибуты действительно могут быть достойным решением (конечно, это означает дублирование). Может быть, есть какие-то (нетехнические) рекомендации по обработке таких зависимостей?

3. Было бы болезненно с точки зрения документации посещать потенциально сотни классов только для того, чтобы узнать, какие зависимости от служб пользователь должен зарегистрировать после загрузки модуля? Не было бы более полезным реализовать список необходимых служб внутри самого модуля, сохраняя его централизованно определенным?