可注入的单例服务还不是 new()able .net core
Injectable singleton service yet not new()able .net core
使用 .net-core DI 框架,我将我的 Foo 服务注册为单例:
services.AddSingleton<IFoo, Foo>();
但是,我不希望任何其他开发人员创建 Foo 服务的新实例:
var storage = new TokenStorage(); // bad practice, not allowed
我知道我可以使用如下不可访问的构造函数在内部使 Foo 服务成为单例:
public class Foo : IFoo
{
private static _instance;
protected Foo() {}
public static IFoo GetInstance()
{
return _instance ??= new Foo();
}
}
但我不想使用 Foo.GetInstance().Bar()[ 访问此服务的传统方式=13=]
简而言之,我希望 DI 框架使 Foo 成为单例,同时让开发人员在不使用单例模式的情况下通过 new Foo() 实例化它。
有什么方法或模式可以实现吗?
using Microsoft.Extensions.DependencyInjection;
using System;
namespace ConsoleApp1
{
interface IFoo { int FooProp { get; } }
interface IBar { int BarProp { get; } }
class Foo : IFoo { public int FooProp => 10; }
class Bar : IBar { public int BarProp => 10; }
class Program
{
static readonly ServiceProvider _serviceProvider = ConfigureServices().BuildServiceProvider();
static IServiceCollection ConfigureServices() =>
new ServiceCollection()
.AddSingleton<IFoo, Foo>()
.AddTransient<IBar, Bar>();
static void Main(string[] args)
{
ConfigureServices();
var foo1 = _serviceProvider.GetRequiredService<IFoo>();
var foo2 = _serviceProvider.GetRequiredService<IFoo>();
var bar1 = _serviceProvider.GetRequiredService<IBar>();
var bar2 = _serviceProvider.GetRequiredService<IBar>();
Console.WriteLine($"foo1 & foo2 are {(foo1 == foo2 ? "same" : "different")} instances.");
Console.WriteLine($"bar1 & bar2 are {(bar1 == bar2 ? "same" : "different")} instances.");
}
}
}
结果:
foo1 & foo2 是相同的实例。
bar1 & bar2 是不同的实例。
使用单例模式实现Foo
,然后在注册时provide a factory function that retrieves the singleton instance IFoo
:
services.AddSingleton<IFoo, Foo>(_ => Foo.GetInstance());
将 Foo
的实施移至包含您的 Composition Root 的项目。由于该项目已经依赖于应用程序中的所有其他项目,因此不可能(不手动破解项目文件)让这些项目引用启动项目(因为这会导致循环依赖)。这有效地隐藏了所有其他项目的 Foo
。这些项目只能依赖于 IFoo
抽象,它们绝不会意外创建 Foo
.
的新实例
如果 Foo
包含太多逻辑无法移动,或者,您可以使 Foo
抽象并在组合根中创建一个 FooImpl
派生并注册它。效果是一样的;其他项目中的代码无法创建 FooImpl
,因为它们没有对启动项目的引用。
此方法使您不必使用单例设计模式,这在 Foo
的情况下是一个 Volatile Dependency(很可能是因为您将其隐藏在抽象背后)。
使用 .net-core DI 框架,我将我的 Foo 服务注册为单例:
services.AddSingleton<IFoo, Foo>();
但是,我不希望任何其他开发人员创建 Foo 服务的新实例:
var storage = new TokenStorage(); // bad practice, not allowed
我知道我可以使用如下不可访问的构造函数在内部使 Foo 服务成为单例:
public class Foo : IFoo
{
private static _instance;
protected Foo() {}
public static IFoo GetInstance()
{
return _instance ??= new Foo();
}
}
但我不想使用 Foo.GetInstance().Bar()[ 访问此服务的传统方式=13=]
简而言之,我希望 DI 框架使 Foo 成为单例,同时让开发人员在不使用单例模式的情况下通过 new Foo() 实例化它。
有什么方法或模式可以实现吗?
using Microsoft.Extensions.DependencyInjection;
using System;
namespace ConsoleApp1
{
interface IFoo { int FooProp { get; } }
interface IBar { int BarProp { get; } }
class Foo : IFoo { public int FooProp => 10; }
class Bar : IBar { public int BarProp => 10; }
class Program
{
static readonly ServiceProvider _serviceProvider = ConfigureServices().BuildServiceProvider();
static IServiceCollection ConfigureServices() =>
new ServiceCollection()
.AddSingleton<IFoo, Foo>()
.AddTransient<IBar, Bar>();
static void Main(string[] args)
{
ConfigureServices();
var foo1 = _serviceProvider.GetRequiredService<IFoo>();
var foo2 = _serviceProvider.GetRequiredService<IFoo>();
var bar1 = _serviceProvider.GetRequiredService<IBar>();
var bar2 = _serviceProvider.GetRequiredService<IBar>();
Console.WriteLine($"foo1 & foo2 are {(foo1 == foo2 ? "same" : "different")} instances.");
Console.WriteLine($"bar1 & bar2 are {(bar1 == bar2 ? "same" : "different")} instances.");
}
}
}
结果: foo1 & foo2 是相同的实例。 bar1 & bar2 是不同的实例。
使用单例模式实现Foo
,然后在注册时provide a factory function that retrieves the singleton instance IFoo
:
services.AddSingleton<IFoo, Foo>(_ => Foo.GetInstance());
将 Foo
的实施移至包含您的 Composition Root 的项目。由于该项目已经依赖于应用程序中的所有其他项目,因此不可能(不手动破解项目文件)让这些项目引用启动项目(因为这会导致循环依赖)。这有效地隐藏了所有其他项目的 Foo
。这些项目只能依赖于 IFoo
抽象,它们绝不会意外创建 Foo
.
如果 Foo
包含太多逻辑无法移动,或者,您可以使 Foo
抽象并在组合根中创建一个 FooImpl
派生并注册它。效果是一样的;其他项目中的代码无法创建 FooImpl
,因为它们没有对启动项目的引用。
此方法使您不必使用单例设计模式,这在 Foo
的情况下是一个 Volatile Dependency(很可能是因为您将其隐藏在抽象背后)。