【问题标题】:IServiceProvider does not return a Singleton instanceIServiceProvider 不返回 Singleton 实例
【发布时间】:2017-04-28 09:17:53
【问题描述】:

我正在使用.Net Core,目前正面临它的 DI 引擎。

我的项目是一个类库,所以在这里asp的绑定是无关紧要的。

我按照this article 的提示来使 DI 工作。

现在,进入精彩部分:

我做了一个如下图的服务提供者:

public static class ServiceProvider
    {
        public static IServiceProvider GetServiceProvider()
        {
            var services = new ServiceCollection();

            //Singletons
            services.AddSingleton<IInstance, Instance>();

            //Transients
            services.AddTransient<IDate, Date>();
            services.AddTransient<IMath, Math>();
            services.AddTransient<INumber, Number>();
            return services.BuildServiceProvider();
        }
    }

然后我尝试在如下所示的静态类中执行它:

public static class MySingleton
    {
        public static IInstance Instance
            => ServiceProvider.GetServiceProvider().GetService<IInstance>();
    }

为了测试单例行为,我已经这样测试了:

[Test]
        public void Instance_HundredTimes_ReturnsSameInstance()
        {
            //Arrange
            const int callCount = 100;
            var results = new IInstance[callCount];

            //Act
            for (var i = 0; i < callCount; i++)
            {
                results[i] = MySingleton.Instance;
            }

            //Assert
            Assert.That(results.Distinct().Count(), Is.EqualTo(1));
        }

测试结果是否定的,并且在 distinct 之后没有单个引用(因为它是单例),我仍然有一百个实例。

我做错了吗?

我是否遗漏了配置中的任何内容?

【问题讨论】:

  • 每次访问MySingleton.Instance(通过调用ServiceProvider.GetServiceProvider())时,您都会创建一个IServiceProvider 的新实例。
  • 我假设BuildServiceProvider() 将我的配置保持为单例......如果是这种情况,我必须延迟加载或简单地维护提供程序的静态实例,一种或另一种方式?
  • 基于源代码,我没有看到任何 static 修改器在 DI 中涉及的类上会使 ServiceProvider 成为共享的。是的,如果您尚未提供 ServiceProvider 的单个实例,则由您自己决定。
  • 好的,所以不能完全避免 static/lazy 代码...不过谢谢,感谢您的帮助!

标签: c# .net dependency-injection .net-core


【解决方案1】:

您的问题出在此代码块中:

public static class MySingleton
{
    public static IInstance Instance
        => ServiceProvider.GetServiceProvider().GetService<IInstance>();
}

您在这里所做的就是致电ServiceProvider.GetServiceProvider().GetService&lt;IInstance&gt;()每次访问该属性时。这意味着实际上并没有 单例。您要做的是设置一次单例实例:

public static class MySingleton
{
    public static IInstance Instance
    {
        get;
    } = ServiceProvider.GetServiceProvider().GetService<IInstance>();
}

此外,您在创建服务时也会遇到类似的问题。每次调用 GetServiceProvider() 时都会创建一个新的 ServiceCollection。你可能不想要那个。你可能想要这样的东西:

public static class ServiceProvider
{
    private static IServiceProvider serviceProvider = null;

    public static IServiceProvider GetServiceProvider()
    {
        if (serviceProvider == null) 
        {
            var services = new ServiceCollection();

            //Singletons
            services.AddSingleton<IInstance, Instance>();

            //Transients
            services.AddTransient<IDate, Date>();
            services.AddTransient<IMath, Math>();
            services.AddTransient<INumber, Number>();

            serviceProvider = services.BuildServiceProvider();
        }
        return serviceProvider;
    }
}

【讨论】:

  • AddSingleton&lt;TInterface,TImplementation&gt;() 的全部意义在于它添加了一个我不应该自己管理的单例..
  • 正如 kiziu 已经提到的,return services.BuildServiceProvider(); 是我的问题
  • @shirbr510:那么您可能不应该在GetServiceProvider 中创建新的服务集合。这也在我的帖子中。
  • 如果我错了请纠正我,但是如果我需要保留IServiceProvider 的实例,为什么保留ServiceCollection 很重要?
  • @shirbr510:你为什么要在每次通话时再次构建服务证明者。检查我对答案的更新。
【解决方案2】:

问题:

我的问题在于没有将我的 IserviceProvider 实例保留在 MySingleton 中

解决方案:

TL;DR

拥有IserviceProvider的静态实例

代码重构

ServiceProvider 重构为如下所示:

公共类 LodashServiceProvider { 私有静态只读 Lazy LazyServiceProvider;

    static LodashServiceProvider()
    {
        LazyServiceProvider = new Lazy<IServiceProvider>(InitializeServiceProvider);
    }

    public static IServiceProvider GetServiceProvider() => LazyServiceProvider.Value;

    private static IServiceProvider InitializeServiceProvider()
    {
        var services = new ServiceCollection();

        //Singletons
        services.AddSingleton<ILodashInstance, LodashInstance>();

        //Transients
        services.AddTransient<ILodashDate, LodashDate>();
        services.AddTransient<ILodashMath, LodashMath>();
        services.AddTransient<ILodashNumber, LodashNumber>();
        return services.BuildServiceProvider();
    }
}

我要感谢 KiziuSefe 对问题的解决方案。

【讨论】:

    猜你喜欢
    • 2017-06-04
    • 2015-01-10
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    相关资源
    最近更新 更多