Использование служб Scoped в размещенной службе в ASP.NET Core

У меня есть служба, которой я хочу поделиться с другими временными службами. Прямо сейчас это не совсем сервис, но в реальном приложении он будет. Как я могу поделиться своим сервисом, используя внедрение зависимостей?

Я добавил демонстрационный код ниже. SharedService должен быть одним и тем же объектом для MyTransientService1 и MyTransientService2 в области видимости MyCreatorService.

Второе утверждение терпит неудачу, хотя это то, чего я хотел бы достичь.

    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        private static IHostBuilder CreateHostBuilder(string[] args)
            => Host.CreateDefaultBuilder(args)
                .ConfigureServices((_, services) =>
                {
                    services.AddScoped<SharedService>();
                    services.AddTransient<MyTransientService1>();
                    services.AddTransient<MyTransientService2>();
                    services.AddTransient<MyCreatorService>();
                    services.AddHostedService<MyHostedService>();
                });
    }

    public class SharedService
    {
        public Guid Id { get; set; }
    }

    public class MyTransientService1
    {
        public SharedService Shared;

        public MyTransientService1(SharedService shared)
        {
            Shared = shared;
        }
    }

    public class MyTransientService2
    {
        public SharedService Shared;

        public MyTransientService2(SharedService shared)
        {
            Shared = shared;
        }
    }

    public class MyCreatorService
    {
        public MyTransientService1 Service1;
        public MyTransientService2 Service2;

        public MyCreatorService(MyTransientService1 s1, MyTransientService2 s2)
        {
            Service1 = s1;
            Service2 = s2;
        }
    }

    public class MyHostedService : BackgroundService
    {
        private readonly IServiceProvider _serviceProvider;

        public MyHostedService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var creator1 = _serviceProvider.GetRequiredService<MyCreatorService>();
            var creator2 = _serviceProvider.GetRequiredService<MyCreatorService>();
            
            Assert.That(creator1.Service1.Shared.Id, Is.EqualTo(creator1.Service2.Shared.Id));
            Assert.That(creator1.Service1.Shared.Id, Is.Not.EqualTo(creator2.Service1.Shared.Id));
            
            return Task.CompletedTask;
        }
    }

person Arnoud Vangrunderbeek    schedule 07.06.2021    source источник
comment
Вы просите решения, которое, кажется, уже предоставлено в вашем вопросе. Можете ли вы объяснить, что не работает с этой настройкой?   -  person Silvermind    schedule 07.06.2021
comment
@Silvermind: второе утверждение не выполняется, и это то, чего я хотел бы добиться. Я хотел бы создать службы, которые каким-то образом совместно используют другую службу. Например, MyHostedService создает два MyCreatorServices. MyCreatorService имеет две временные службы, которые должны использовать общую службу. Но эта общая служба уникальна для MyCreatorService. Надеюсь, это имеет смысл.   -  person Arnoud Vangrunderbeek    schedule 07.06.2021
comment
С MS.DI, если вы разрешаете зависимость Scoped из корневой области, эта Scoped фактически будет Singleton. Это то, что происходит в вашей размещенной службе. Размещенная служба фактически является одноэлементной (даже если вы можете зарегистрировать ее как временную, это не имеет значения), а ее IServiceProvider является корневым провайдером. Вот что является причиной вашей проблемы. Решение состоит в том, чтобы запустить новую область, используя serviceProvider.CreateScope() внутри методов размещенной службы.   -  person Steven    schedule 07.06.2021


Ответы (1)


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

Я также предлагаю вам связать MyHostedService таким образом (если это имеет какое-либо отношение к описанной вами проблеме), поскольку, похоже, он предоставляет Одноэлементная привязка. Если область действия внешней службы (с внедрёнными зависимостями) уже, она будет удерживать в заложниках внедренные зависимости. Таким образом, одноэлементная служба с временными зависимостями сделает все свои зависимости одноэлементными, поскольку внешняя служба будет создана только один раз, а ее зависимости разрешены только один раз.

ОБНОВЛЕНИЕ

После более четкого понимания проблемы это должно сработать для вас (никаких других привязок не требуется):

services.AddTransient<MyCreatorService>(_ =>
            {
                var shared = new SharedService();
                shared.Id = Guid.NewGuid();
                return new MyCreatorService(
                    new MyTransientService1(shared),
                    new MyTransientService2(shared));
            });

person Aage    schedule 07.06.2021
comment
Мне нужно, чтобы другие службы (MyTransientService1 и MyTransientService2) создавались каждый раз, когда они разрешаются, и им нужно совместно использовать SharedService. Можете ли вы дать мне более подробную информацию о том, как это сделать? Заранее большое спасибо. - person Arnoud Vangrunderbeek; 07.06.2021
comment
Как @silvermind спросил в комментариях, что не работает в вашей настройке? - person Aage; 07.06.2021
comment
Второе утверждение терпит неудачу, и это то, чего я хотел бы достичь. Я хотел бы создать службы, которые каким-то образом совместно используют другую службу. Например, MyHostedService создает два MyCreatorServices. MyCreatorService имеет две временные службы, которые должны использовать общую службу. Но эта общая служба уникальна для MyCreatorService. Надеюсь, это имеет смысл. - person Arnoud Vangrunderbeek; 07.06.2021
comment
В вашем примере должно быть два разных экземпляра MyCreatorService, использующих один и тот же экземпляр SharedService. Разве это не то, что вы хотите? Ваш второй Assert терпит неудачу, потому что они являются одним и тем же Id. - person Aage; 07.06.2021
comment
Да, я знаю, но я хочу, чтобы они были другими. Смотрите, я хочу, чтобы MyCreatorService имел MyTransientService1 и MyTransientService2, совместно использующие один и тот же MySharedService. Таким образом, для каждого нового MyCreatorService должен быть новый MySharedService, который совместно используется только что созданными MyTransientService1 и MyTransientService2. - person Arnoud Vangrunderbeek; 07.06.2021
comment
Можете ли вы проверить мой обновленный ответ? - person Aage; 07.06.2021
comment
MyCreatorService в моей реальной установке может быть создан 100 раз. Поэтому невозможно создать разные его экземпляры. - person Arnoud Vangrunderbeek; 07.06.2021
comment
Давайте продолжим обсуждение в чате. - person Aage; 07.06.2021