服务的生命周期
1.AddTransient类型的声明周期对象
声明一个测试的接口和测试用到的类:
public interface ITestService
{
public string Name { get; set; }
public void SayHi();
}
public class TestServiceImpl1 : ITestService
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"hello,I'm {Name}");
}
}
static void Main(string[] args)
{
//生命周期
{
ServiceCollection services = new ServiceCollection(); //用来构建容器对象
services.AddTransient<TestServiceImpl1>();
using (ServiceProvider sp = services.BuildServiceProvider()) //ServiceProvider 服务定位器
{
TestServiceImpl1 t = sp.GetService<TestServiceImpl1>();
t.Name = "jam";
t.SayHi();//jam
TestServiceImpl1 t1 = sp.GetService<TestServiceImpl1>();
t1.Name = "tom";
t1.SayHi();//tom
t.SayHi();//jam
Console.WriteLine(object.ReferenceEquals(t, t1));//false
}
}
Console.Read();
}
执行结果:
AddTransient类型的声明周期对象,每次调用都会生成一个新的对象。
2.AddSingleton类型的声明周期对象
static void Main(string[] args)
{
//生命周期 AddSingleton
{
ServiceCollection services = new ServiceCollection(); //用来构建容器对象
services.AddSingleton<TestServiceImpl1>();
using (ServiceProvider sp = services.BuildServiceProvider()) //ServiceProvider 服务定位器
{
TestServiceImpl1 t = sp.GetService<TestServiceImpl1>();
t.Name = "jam";
t.SayHi();//jam
TestServiceImpl1 t1 = sp.GetService<TestServiceImpl1>();
t1.Name = "tom";
t1.SayHi();//tom
t.SayHi();//tom
Console.WriteLine(object.ReferenceEquals(t, t1));//true
}
}
Console.Read();
}
执行结果:
AddSingleton类型的声明周期对象,每次调用都是同一个对象。
3.AddScoped类型的声明周期对象
static void Main(string[] args)
{
//生命周期 AddScoped
{
ServiceCollection services = new ServiceCollection(); //用来构建容器对象
services.AddScoped<TestServiceImpl1>();
using (ServiceProvider sp = services.BuildServiceProvider()) //ServiceProvider 服务定位器
{
using (IServiceScope scope = sp.CreateScope())
{
//在scope中获取scope相关的对象,而不是sp
TestServiceImpl1 t = scope.ServiceProvider.GetService<TestServiceImpl1>();
t.Name = "jam";
t.SayHi();//jam
TestServiceImpl1 t1 = scope.ServiceProvider.GetService<TestServiceImpl1>();
t1.Name = "tom";
t1.SayHi();//tom
t.SayHi();//tom
Console.WriteLine(object.ReferenceEquals(t, t1));//true
}
}
}
Console.Read();
}
执行结果:
static void Main(string[] args)
{
//生命周期 AddScoped
{
ServiceCollection services = new ServiceCollection(); //用来构建容器对象
services.AddScoped<TestServiceImpl1>();
using (ServiceProvider sp = services.BuildServiceProvider()) //ServiceProvider 服务定位器
{
TestServiceImpl1 test;
using (IServiceScope scope = sp.CreateScope())
{
//在scope中获取scope相关的对象,而不是sp
TestServiceImpl1 t = scope.ServiceProvider.GetService<TestServiceImpl1>();
t.Name = "jam";
t.SayHi();//jam
TestServiceImpl1 t1 = scope.ServiceProvider.GetService<TestServiceImpl1>();
t1.Name = "tom";
t1.SayHi();//tom
t.SayHi();//tom
Console.WriteLine(object.ReferenceEquals(t, t1));//true
test = t1;
}
Console.WriteLine("____________________________________");
using (IServiceScope scope2 = sp.CreateScope())
{
//在scope中获取scope相关的对象,而不是sp
TestServiceImpl1 t = scope2.ServiceProvider.GetService<TestServiceImpl1>();
t.Name = "jam";
t.SayHi();//jam
TestServiceImpl1 t1 = scope2.ServiceProvider.GetService<TestServiceImpl1>();
t1.Name = "tom";
t1.SayHi();//tom
t.SayHi();//tom
Console.WriteLine(object.ReferenceEquals(t, t1));//true
//两个不同using范围的对象进行比较
Console.WriteLine(object.ReferenceEquals(test, t1));//false
}
}
}
Console.Read();
}
执行结果:
AddScoped类型的声明周期对象,如果对象在同一个using范围中,每次调用都是同一个对象。
如果在不同的using范围中,调用的则是不同的对象。
总结:
1.使用ServiceProvider.CreateScope()创建scope
2.如果一个类实现了IDisposable接口,则离开作用域后,容器会自动调用对象的Dispose()方法
3.不要在长生命周期对象中引用比它短的生命周期对象。在asp.net core中,这样做默认会抛异常
4.声明周期的选择:如果类无状态,建议用Singleton;如果类有状态,且有Scope,建议用Scoped,因为通常这种Scope控制下的代码都是运行在同一个线程中的,没有并发修改的问题;在使用Transient的时候要谨慎