在ASP.NET Core中,在托管服务中使用作用域服务

5

我有一个服务,我希望在其他短暂服务之间共享。目前它并不真正是一个服务,但在实际应用中它将成为一个服务。如何使用依赖注入来共享我的服务?

我在下面添加了一些演示代码。SharedService应该是MyCreatorService的“Scope”内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;
        }
    }

3
您要求解决方案,而该方案似乎已经在您的问题中提供了。您能否解释一下这个设置有什么问题? - Silvermind
@Silvermind:第二个断言失败了,这就是我想要实现的。我想创建一些以某种作用域方式共享另一个服务的服务。例如,MyHostedService创建了两个MyCreatorServices。MyCreatorService有两个瞬态服务,它们应该共享一个公共服务。但是这个公共服务对于每个MyCreatorService来说是唯一的。希望这样说得清楚明白。 - Arnoud Vangrunderbeek
使用MS.DI,如果您从根作用域解析Scoped依赖项,则该Scoped将有效地成为Singleton。这就是在托管服务中发生的情况。托管服务实际上是一个单例(即使您可能将其注册为瞬态,也无关紧要),其IServiceProvider是根提供程序。这就是导致问题的原因。解决方案是在托管服务的方法中使用serviceProvider.CreateScope()启动新范围。 - Steven
2个回答

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));
            });


像@silvermind在评论中问的那样,你的设置有什么问题? - Aage
第二个断言失败了,这就是我想要实现的。我想创建一些以某种作用域方式共享另一个服务的服务。例如,MyHostedService 创建了两个 MyCreatorServices。MyCreatorService 有两个瞬态服务,它们应该共享一个公共服务。但是这个公共服务对于每个 MyCreatorService 是唯一的。希望这样说得清楚明白。 - Arnoud Vangrunderbeek
你的示例应该有两个不同的MyCreatorService实例,使用相同的SharedService实例。这不是你想要的吗?你的第二个断言失败了,因为它们相同的Id - Aage
是的,我知道,但我希望它们不同。我想让MyCreatorService拥有MyTransientService1和MyTransientService2,这两个服务共享同一个MySharedService。因此,每个“新”的MyCreatorService都应该有一个“新”的MySharedService,它在新创建的MyTransientService1和MyTransientService2之间共享。 - Arnoud Vangrunderbeek
我的MyCreatorService在实际设置中可能会被创建数百次。因此,不可能创建不同的实例。 - Arnoud Vangrunderbeek
显示剩余2条评论

0
  1. SharedService 类添加一个构造函数,否则 Id 始终为 000

  2. 使用以下代码创建不同的作用域,这样 SharedService 将被初始化两次:

    MyCreatorService creator1;
    MyCreatorService creator2;
    using (var scope1 = _serviceProvider.CreateScope())
    {
      creator1 = scope1.ServiceProvider.GetRequiredService<MyCreatorService>();
    }
    using (var scope2 = _serviceProvider.CreateScope())
    {
      creator2 = scope2.ServiceProvider.GetRequiredService<MyCreatorService>();
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接