0

У меня есть собственная реализация брокера сообщений (по типу MessagePipe), там все тоже реализовано через DI и использование интерфейсов типа IReceiver<T> (для получения), ISender<T> (для отправки).

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

Как обойти это? Просто мне было интересно реализовать приложение, которое работает полностью через события. Есть конечно дешево-сердитый вариант - собственноручно запрашивать экземпляры через GetRequiredService<T>() после сборки контейнера. Мне в голову приходит еще вариант добавить к таким сервисам какой-нибудь интерфейс и уже по интерфейсу запрашивать все классы, но адекватно ли это?

задан 10 нояб. 2025 в 23:15
14
  • 3
    DI от Microsoft? IHostedService вам поможет. Commented 10 нояб. 2025 в 23:58
  • Да, что за DI то используется? Там есть всякие настройки и варианты. Контейнеры обычно довольно гибко настраиваются под любые нужды. Commented 11 нояб. 2025 в 6:40
  • Ну так при использовании и возникнет "востребование" - и все инстанцируется. Commented 11 нояб. 2025 в 8:32
  • Хм... А зачем тут вообще DI? Он будет полезен, если у вас несколько независимых брокеров (чего бывает очень редко). Для одного брокера отлично подойдет и статика (как это сделано у брокера от Microsoft). Commented 11 нояб. 2025 в 10:00
  • @vitidev, так я не хочу явно связывать классы между собой, идея такова, чтобы они там сами крутились, получая-отправляя сообщения, но если явно не затребовать экземпляр класса через средства DI, то он не будет создан (и не получит свои pub/sub от обменника, а значит и получать-передавать не сможет), пока что есть несколько вариантов: 1. внедрить классы в один точно инстанцируемый класс. 2. накинуть интерфейс и через IEnumerable<интерфейс> запросить. 3. IHostedService, как предложили выше, как по мне лучший из Commented 12 нояб. 2025 в 6:42

1 ответ 1

0

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

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

По стандарту DI-контейнеры используют ленивую инициализацию (lazy loading), т.е. объект создается только в тот момент, когда он потребуется. Так банально эффективнее, но в моем случае классы явно не связаны друг с другом, поэтому момент, когда они потребуются не наступит, а следовательно не будут и созданы подписки. Поэтому нужно как-то автоматически активировать нужные классы.

  1. Использовать авто-активацию через Microsoft.Extensions.DependencyInjection.AutoActivation (или Autofac) (мой выбор)

Ниже будет пример кода для Microsoft, узнал я о таком функционале вот здесь -> issue. Я немного удивлен, что это было реализовано относительно недавно в .NET 8.0.

builder.Services.AddActivatedSingleton<T>();

Для Autofac все еще проще и поддерживается из коробки. Подробнее тут -> документация.

containerBuilder.RegisterType<T>()
 .AsSelf()
 .AutoActivate();
  1. Использовать IHostedService (или IStartable)

Еще можно сделать каждый сервис, как IHostedService, тогда будет вызван метод StartAsync, в котором должна будет выполняться бесконечно долгая задача по типу:

while(!cts.IsCancellationRequested)
{
 ...
}

В противном случае Task StartAsync завершится и сервис будет остановлен. Еще я не уверен, что будет, если один IHostedService зависит от другого. Решает ли что-то в этом случае Microsoft DI.

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

  1. Инициализация ручками

Это самый простой способ. У нас по любому есть общая точка, которая 100% будет существовать. Поэтому нужные классы можно вызвать ручками. Первый способ - это во время запуска приложения. Второй способ - в любом классе программы.

private static IServiceCollection Instantiate(this IServiceCollection services) // Mircosoft.Extensions.DependencyInjection
{
 using var provider = services.BuildServiceProvider();
 
 _ = provider.GetRequiredService<T>(); // для каждого класса
 
 return services;
}
private static ContainerBuilder Instantiate(this ContainerBuilder builder) // Autofac
{
 using var container = builder.Build();
 
 _ = container.Resolve<T>(); // для каждого класса
 
 return builder;
}

Второй способ - это любая точка внедрения, куда сможет это сделать DI.

public Foo(Boo boo, Goo goo, Loo loo) { ... } // через конструктор
public void Foo(Boo boo, Goo goo, Loo loo) { ... } // через метод

И через иные точки внедрения зависимости: атрибуты, свойства и так далее...

  1. Через атрибут

Нужно сделать атрибут, условно AutoActivate и при помощи инструментов, таких как Scrutor можно делать это как-то вроде так.

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AutoActivateAttribute : Attribute
{
 ...
}
public static IServiceCollection AddAutoActivateServices(this IServiceCollection services)
{
 services.Scan(scan => scan
 .FromAssemblyOf<Program>()
 .AddClasses(classes => classes.WithAttribute<AutoActivateAttribute>())
 .UsingRegistrationStrategy(RegistrationStrategy.Skip)
 .AsSelfWithInterfaces()
 );
 using var provider = services.BuildServiceProvider()
 _ = GetRequiredService<T>();
 
 return services;
}
  1. Через общий интерфейс

Тоже самое, но только ищем по интерфейсу, затем получаем просто IEnumerable<Наш_интерфейс> и дело готово.

  1. Через атрибут + интерфейс

Тут вариант в виде комбинации двух подходов выше (4 и 5). Я описывать его не буду, потому что всё, начиная с 4 это буквально самостоятельная реализация велосипеда. И как бы раз я уже нашел хороший способ, то на нем и поеду.

ответ дан 12 нояб. 2025 в 22:25

Ваш ответ

Черновик сохранён
Черновик удалён

Зарегистрируйтесь или войдите

Регистрация через Google
Регистрация через почту

Отправить без регистрации

Необходима, но никому не показывается

Отправить без регистрации

Необходима, но никому не показывается

Нажимая «Отправить ответ», вы соглашаетесь с условиями пользования и подтверждаете, что прочитали политику конфиденциальности.

Начните задавать вопросы и получать на них ответы

Найдите ответ на свой вопрос, задав его.

Задать вопрос

Изучите связанные вопросы

Посмотрите похожие вопросы с этими метками.