Должны ли модули Autofac регистрировать свои собственные зависимые модули?
Предположим, у меня есть приложение, которое использует три библиотеки: lib1, lib2 и lib3. В каждой из библиотек я реализовал Module
который регистрирует зависимости, которые реализованы в этой библиотеке.
Некоторые из этих реализаций имеют свои собственные зависимости, например, lib2 и lib3 могут потребоваться некоторые реализации, существующие в lib1.
Мой вопрос: разрешить ли модулю в lib2 и lib3 зарегистрировать модуль в lib1 как часть их Load
реализация? Это собирается зарегистрировать этот модуль дважды, если мое приложение регистрирует модули lib2 и lib3?
Или я воздерживаюсь от того, чтобы позволить модулю зарегистрировать другой модуль, оставив его на усмотрение приложения с тем недостатком, что некоторые регистрации могут отсутствовать при запуске?
4 ответа
Autofac Module
не более, чем фасад, чтобы помочь в регистрации компонентов. Он не должен вызывать другие модули с целью регистрации реализаций, которые, как известно, являются зависимостями своих собственных регистраций, потому что это связало бы два модуля вместе, увеличивая связь.
Кроме того, если один и тот же модуль зарегистрирован несколько раз, сами регистрации применяются несколько раз, поскольку Autofac не отслеживает модули, которые уже были зарегистрированы. Обычно это не создает проблемы, если корень композиции случайно не аннулирует какие-либо регистрации, предназначенные для переопределения существующих.
Немного поздно я знаю, но я подумал, что было бы неплохо ответить на вопрос, а не просто цитировать Священное Писание о том, как многократные корни композиции являются злом...
Это собирается зарегистрировать этот модуль дважды, если мое приложение регистрирует модули lib2 и lib3?
Протестировано с Autofac 4.8.1 и оно делает:
public class Lib1Module: Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine("Registering Lib1Module");
// Register Types...
}
}
public class Lib2Module: Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine("Registering Lib2Module");
builder.RegisterModule<Lib1Module>();
// Register Types...
}
}
public class Lib3Module: Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine("Registering Lib3Module");
builder.RegisterModule<Lib1Module>();
// Register Types...
}
}
public class Program
{
public void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule<Lib2Module>();
builder.RegisterModule<Lib3Module>();
using(var container = builder.Build())
{
// Do Stuff
}
}
}
Выходы:
Registering Lib2Module
Registering Lib1Module
Registering Lib3Module
Registering Lib1Module
Вы можете использовать словарь свойств на IComponentRegistry
/ContainerBuidler
(The Module
базовый класс внутренне создает конструктор контейнеров, который он передает Load
со свойствами из IComponentRegistry
источник), чтобы обойти это и заставить единичные регистрации для модулей, если Load
Метод Lib1Module
изменено на:
protected override void Load(ContainerBuilder builder)
{
if (builder.Properties.ContainsKey(GetType().AssemblyQualifiedName))
{
return;
}
builder.Properties.Add(GetType().AssemblyQualifiedName, null);
Console.WriteLine("Registering Lib1Module");
// Register Types...
}
Тогда вывод становится:
Registering Lib2Module
Registering Lib1Module
Registering Lib3Module
Очевидно, что если Lib2Module/Lib3Module могут стать зависимостями других модулей, аналогичный код должен быть вставлен в их метод Load и аналогично, если используются какие-либо модули. AttachToRegistrationSource
и / или AttachToComponentRegistration
и хотел убедиться, что их запускают только после того, как им также понадобится проверка. В качестве альтернативы (и, вероятно, желательно, если это то, что вам нужно сделать много), вы можете создать свой собственный класс реализации IModule
и сделать проверку в течение Configure
,
Я определенно использовал этот шаблон в производственном коде, чтобы уменьшить количество повторений, когда у меня есть несколько точек входа с их собственными корнями композиции (например, веб-API, веб-приложение и повторяющееся консольное приложение), которые, тем не менее, разделяют большой кусок кода, и я могу жить с этим, делая меня персоной нон грата среди DI пуристов.
В общем, должна быть только одна библиотека, которая содержит конфигурации. Эта библиотека является начальным проектом, и это место в приложении, где все подключено, обычно называют корнем композиции. Обычно только запускаемые проекты имеют корень композиции, и только когда несколько запускаемых проектов в одном решении совместно используют много повторяющихся регистраций, вы начинаете извлекать этот код в общее место, которое эти корни композиции могут использовать повторно. Но будьте осторожны: в целом корни композиции не должны использоваться повторно.
Я не буду рекомендовать делать регистрации внутри ваших библиотек. В большинстве случаев у вас должен быть один корень композиции, который будет составлять все ваше приложение.
Корень композиции - это (предпочтительно) уникальное место в приложении, где модули составляются вместе.
Эта концепция объясняется здесь композицией root.
Кстати, если вы зарегистрируете модуль несколько раз, Autofac зарегистрирует компонент многократного времени. Если у вас должен быть модуль внутри вашей библиотеки, вы должны только создать модуль, который регистрирует компонент библиотеки.