在前面的章节中我们有看到了 AddScoped AddTransient 和 AddSingleton 这三种方式来添加依赖注入的配置.
单例模式是比较好理解的。不管哪一个类需要这个依赖项,这个对象在整个系统中只会有一个实例。
这个模式是比较特殊的,就是同一个scope下面只会创建一次。 假设我们有如下三个类
public class Worker
{
private readonly IMessageWriter _messageWriter;
public Worker(IMessageWriter messageWriter)
{
_messageWriter = messageWriter;
Console.WriteLine("worker:" + _messageWriter.GetHashCode());
}
}
public class Worker2
{
private readonly IMessageWriter _messageWriter;
public Worker2(IMessageWriter messageWriter, InnerWorker innerWorker)
{
_messageWriter = messageWriter;
Console.WriteLine("worker2:" + _messageWriter.GetHashCode());
}
}
public class InnerWorker
{
private readonly IMessageWriter _messageWriter;
public InnerWorker(IMessageWriter messageWriter)
{
_messageWriter = messageWriter;
Console.WriteLine("InnerWorker:" + _messageWriter.GetHashCode());
}
}
public static class DIConfig
{
public static IServiceProvider ConfigureServices()
{
var serviceCollection = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", true)
.AddEnvironmentVariables()// 把环境变量也放到 Configuraiton当中
.Build();
serviceCollection.AddSingleton((IConfiguration)configuration);
serviceCollection.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConfiguration(configuration.GetSection("Logging")); //配置logging的一些东西
// 下面的这行需要 Microsoft.Extensions.Logging.Console
loggingBuilder.AddConsole(); //加多个 每一个Ilooger下面就会有多个provider
});
// 注入了一个默认的ILogger
serviceCollection.AddSingleton<Microsoft.Extensions.Logging.ILogger>((it) =>
{
return it.GetService<ILoggerFactory>().CreateLogger("categtory");
});
Register(serviceCollection, configuration);
return serviceCollection.BuildServiceProvider();
}
private static void Register(IServiceCollection services, IConfiguration configuration)
{
//注入方式变动的地方
services.AddScoped<IMessageWriter, MessageWriter>();
services.AddTransient<InnerWorker>();
services.AddTransient<Worker>();
services.AddTransient<Worker2>();
services.AddTransient(typeof(Lazy<>),typeof(LazyLoader<>));
services.AddScoped(typeof(IMalemaLogger<>), typeof(MalemaLogger<>));
services.AddSingleton(sp =>
{
var conn = configuration.GetConnectionString("malema");
return new Func<MalemaDbContext>(() =>
{
var builder = new DbContextOptionsBuilder<MalemaDbContext>();
builder.UseSqlServer(conn);
return new MalemaDbContext(builder.Options);
});
});
}
}
当我们用下面的方式配置时
services.AddScoped<IMessageWriter, MessageWriter>();
services.AddScoped<InnerWorker>();
services.AddSingleton<Worker>();
services.AddSingleton<Worker2>();
static async Task Main(string[] args)
{
var sp = DIConfig.ConfigureServices();
var worker = sp.GetService<Worker>();
var worker2 = sp.GetService<Worker2>();
var scope = sp.CreateScope();
var scopeWorker = scope.ServiceProvider.GetService<Worker>();
var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
}
会输出
worker:65204782
InnerWorker:65204782
worker2:65204782
Scope分割线--------------
注: Asp.net core 的场景下,Singleton 所依赖的接口如果被注册为Scoped时会报错。 Lifetime: Singleton ImplementationType: : Cannot consume scoped service
当我们用下面的方式配置时
services.AddScoped<IMessageWriter, MessageWriter>();
services.AddScoped<InnerWorker>();
services.AddSingleton<Worker>();
services.AddScoped<Worker2>();
//跟前面一样没有变化
static async Task Main(string[] args)
{
var sp = DIConfig.ConfigureServices();
var worker = sp.GetService<Worker>();
var worker2 = sp.GetService<Worker2>();
var scope = sp.CreateScope();
var scopeWorker = scope.ServiceProvider.GetService<Worker>();//同一个scope出来的
var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();//同一个scope出来的
}
会输出
worker:65204782
InnerWorker:65204782
worker2:65204782 //虽然注入的是scope并且MessageWriter也是scope 但实际上就是根的scope
Scope分割线--------------
InnerWorker:49972132 //新scoped 创建出来不一样了,但是跟worker2的是一样的
worker2:49972132 //新scoped 创建出来不一样了
备注: Asp.net Controller的每一次请求都是一个新Scope。
当我们用下面的方式配置时
services.AddScoped<IMessageWriter, MessageWriter>();
services.AddTransient<InnerWorker>();
services.AddTransient<Worker>();
services.AddTransient<Worker2>();
//跟前面一样没有变化
static async Task Main(string[] args)
{
var sp = DIConfig.ConfigureServices();
var worker = sp.GetService<Worker>();
var worker2 = sp.GetService<Worker2>();
var scope = sp.CreateScope();
var scopeWorker = scope.ServiceProvider.GetService<Worker>();
var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
}
会输出
worker:65204782
InnerWorker:65204782
worker2:65204782
Scope分割线--------------
worker:49972132
InnerWorker:49972132
worker2:49972132
我们可以发现Scope的还是一样的, 虽然调用它的类是Transient。
当下面这种方式注入时
services.AddTransient<IMessageWriter, MessageWriter>();
services.AddScoped<InnerWorker>();
services.AddSingleton<Worker>();
services.AddScoped<Worker2>();
//跟前面一样没有变化
static async Task Main(string[] args)
{
var sp = DIConfig.ConfigureServices();
var worker = sp.GetService<Worker>();
var worker2 = sp.GetService<Worker2>();
var scope = sp.CreateScope();
var scopeWorker = scope.ServiceProvider.GetService<Worker>();
var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
}
会输出
worker:65204782
InnerWorker:49972132
worker2:47096010
Scope分割线--------------
InnerWorker:21210914
worker2:56680499
每一个都是新的。不管有没有在scope或者单例中。