Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: Arnoud VangrunderbeekОтветов: 1

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

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

dependency-injection

Ответы

Ответ 1 · Aage

Если вы используете 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));
            });

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы