【问题标题】:Using Scoped services within a hosted service in ASP.NET Core在 ASP.NET Core 的托管服务中使用 Scoped 服务
【发布时间】:2021-06-07 17:03:58
【问题描述】:

我想在其他临时服务之间共享一项服务。现在它不是真正的服务,但在现实生活中的应用程序中它会。如何使用依赖注入共享我的服务?

我在下面添加了一些演示代码。 SharedService 应该是 MyCreatorService 的“范围”中 MyTransientService1 和 MyTransientService2 的同一个对象。

第二个断言失败,而这是我想要完成的。

    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:第二个断言失败,这就是我想要完成的。我想创建以某种范围方式共享另一个服务的服务。例如,MyHostedService 创建两个 MyCreatorService。 MyCreatorService 有两个临时服务,它们应该共享一个公共服务。但是这个公共服务对于 MyCreatorService 来说是独一无二的。希望这是有道理的。
  • 使用 MS.DI,如果您从根范围解析 Scoped 依赖项,则该 Scoped 将有效地成为单例。这就是您的托管服务中发生的情况。托管服务实际上是一个单例(即使您可能将其注册为瞬态,这并不重要),它的 IServiceProvider 是根提供者。这就是导致您的问题的原因。解决方案是在托管服务的方法中使用serviceProvider.CreateScope() 启动一个新范围。

标签: c# dependency-injection


【解决方案1】:

如果您使用AddScoped,实例将在请求中(例如对于 HTTP 请求和响应)是相同的。如果您需要在每次解析时创建其他服务,您确实可以使用AddTransient,否则您也可以使用AddScoped

我还建议您以这种方式绑定MyHostedService(如果它与您描述的问题有关),因为它似乎提供了Singleton binding。如果外部服务(具有注入依赖项的服务)的范围更窄,它将挟持注入依赖项。因此,具有瞬态依赖的单例服务将使其所有依赖项成为单例,因为外部服务只会创建一次,并且其依赖项只会解析一次。

更新

在更清楚地理解问题之后,这应该对你有用(不需要其他绑定):

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

【讨论】:

  • 我需要在每次解决其他服务(MyTransientService1 和 MyTransientService2)时创建它们,并且它们需要共享 SharedService。你能给我更多关于如何做到这一点的细节吗?提前非常感谢。
  • 就像@silvermind 在 cmets 中问的那样,您的设置有什么问题?
  • 第二个断言失败,这就是我想要完成的。我想创建以某种范围方式共享另一个服务的服务。例如,MyHostedService 创建两个 MyCreatorService。 MyCreatorService 有两个临时服务,它们应该共享一个公共服务。但是这个公共服务对于 MyCreatorService 来说是独一无二的。希望这是有道理的。
  • 您的示例应该使用SharedService相同 实例有两个不同MyCreatorService 实例。这不是你想要的吗?您的第二个断言失败,因为它们相同的Id
  • 是的,我知道,但我希望它们与众不同。请参阅我希望 MyCreatorService 具有 MyTransientService1 和 MyTransientService2 共享相同的 MySharedService。所以对于每一个“新的”MyCreatorService,都应该有一个“新的”MySharedService,在新创建的MyTransientService1和MyTransientService2之间共享。
猜你喜欢
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 2022-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
相关资源
最近更新 更多