Как предоставить IServiceProvider в качестве зависимости, когда вы делаете экземпляры в DI вручную?

контекст

У меня есть 5 услуг:

Service1 (no depends.)
Service2 (no depends.)
Service3 (Service1, Service2, IServiceProvider) : ISpecialService
Service4 (Service1, Service2, Service3) : ISpecialService
Service5 (Service1, Service3, Service4) : ISpecialService

Все они представляют собой синглтон-сервисы по одному экземпляру на каждое приложение. Обычно вы просто добавляете их как синглтоны и BuildServiceProvider. Однако в моем случае все ISpecialService Необходимо зарегистрировать дважды: как заводской, так и как собственный прямой тип, поэтому прямой впрыск через ctor:

public SomeOtherService(Service3 s3, Service4 s4) { }

еще работает. Проблема в том, что если я просто зарегистрирую их нормально:

.AddSingleton<ISpecialService, Service3>()
.AddSingleton<Service3, Service3>()

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

 var s3 = new Service3(s1, s2, ?isp?);
.AddSingleton<ISpecialService, Service3>(s3) // same instance for contract work
.AddSingleton<Service3, Service3>(s3) // same instance
...
.BuildProvider()

И это проблема, потому что Service3 имеет IServiceProvider как зависимость. Обычно он обрабатывается DI, но в этом случае, когда мне нужно дважды зарегистрировать экземпляр SINGLE, я не могу найти способ предоставить полный экземпляр ISP экземпляру Service3. Если я создаю провайдера, я больше не могу добавлять какие-либо сервисы, что является тупиком, потому что Servcie3 позже потребуется запросить обходной путь для циклической зависимости без 99 промежуточных пустых промежуточных бессмысленных сервисов:

private readonly Service1 service1;
private readonly Service2 service2;
private Service4 service4;
private readonly IServiceProvider ISP;

public Service3 (Service1 s1, Service2 s2, IServiceProvider isp)
{
    //assign depenencies
}

public Startup()
{
   //workaround for circular dependency
   service4 = ISP.GetServic<Service4>()
}

Причина, по которой мне нужно зарегистрировать Сервис 3,4,5 в качестве типа интерфейса, заключается в некоторой обычной работе с контактами, которую я хочу сделать позже:

foreach (var srv in  ISP.GetService<ISpecialService>())
{
    serv.Startup();
}

Есть идеи как решить это?

1 ответ

Что вы можете сделать, это:

public class Answer
{
    public Answer()
    {
        // Have your service collection, register everything you need
        // before having to add your custom service.
        var collection = new ServiceCollection()
            .AddSingleton<ISpecialService, Service3>();
            //---

        // Build a disposable container without your custom service
        var provider = collection.BuildServiceProvider();

        // Use that container to call your instance of your custom service
        var serviceInstanceWithDI = provider.GetService<ISpecialService>();
        // This way the service is pulled from the DI container with all of its 
        // dependencies injected, assuming they are registered beforehand

        // Add it back to the collection with its dependencies already injected
        collection.AddSingleton<Service3, Service3>(serviceInstanceWithDI);

        // Now you can build the IServiceProvider and have your 2 instances registered.
        var serviceProvider = collection.BuildServiceProvider();
    }
}
Другие вопросы по тегам