作用:管理类与类之间的依赖关系,帮我们构造类、确保我们代码的可维护性和可扩展性
ASP.Net Core 中,主要是用来管理我们对象的依赖、生命周期,负责各个组件之间的协作
ASP.NET 自带的依赖注入框架组件包:内置
- Microsoft.Extensions.DependencyInjection.Abstractions
- Microsoft.Extensions.DependencyInjection
这两个:一个是抽象包、一个是具体的实现,它们用到了一个设计模式:接口实现分离模式,即:我们的组件只需要依赖抽象它的抽象接口,而不需要它的实现;当使用的时候注入它的具体实现即可;
好处是:可以在使用时决定我们用具体哪个实现,未来我们可以去做任意的扩展替换具体的依赖注入框架的具体实现;
依赖注入框架的核心类型有:
- IServiceCollection:负责服务的注册
- ServiceDescriptor:每一个服务注册时的信息
- IServiceProvider:具体的容器,由 ServiceCollection Build出来的
- IServiceScope:表示一个容器的子容器的生命周期
依赖注入的生命周期
- 单例 Singleton:只有一个对象,无论获取多少次
- 作用域 Scoped:在Scope的生存周期内(也就是容器的生存周期内、活着子容器的生存周期内),得到的一个单例模式,而如果容器释放掉,那么对应的对象也会被释放掉;
- 瞬时(暂时) Tranient :每次从容器中获取的对象都是全新的对象
public interface IMySingletonService { }
public class MySingletonService: IMySingletonService
{}
public interface IMyScopeService { }
public class MyScopeService : IMyScopeService
{}
public interface IMyTransientService { }
public class MyTransientService : IMyTransientService
{}
将上面注入到容器中:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopeService, MyScopeService>();
services.AddTransient<IMyTransientService, MyTransientService>();
Console.WriteLine("Startup.ConfigureServices");
services.AddControllers();
}
在控制器中的Action中使用:
[HttpGet]
public int GetService([FromServices]IMySingletonService singleton1, // FromServices 的作用就是从容器中获取对象
[FromServices]IMySingletonService singleton2,
[FromServices]IMyScopeService scope1,
[FromServices]IMyScopeService scope2,
[FromServices]IMyTransientService transient1,
[FromServices]IMyTransientService transient2,)
{
// 输出对象的hash指(如果两个对象的hash值相同,那么就可以说这两个对象就是同一个对象)
Console.WriteLine($"singleton1:{singleton1.GetHashCode()}");
Console.WriteLine($"singleton2:{singleton2.GetHashCode()}");
Console.WriteLine($"transient1:{transient1.GetHashCode()}");
Console.WriteLine($"transient2:{transient2.GetHashCode()}");
Console.WriteLine($"scope1:{scope1.GetHashCode()}");
Console.WriteLine($"scope2:{scope2.GetHashCode()}");
}
运行,再刷新一次,对比两次输出结果;
服务注册方式
1、除了用上面提到的 泛型方式注入我们的服务 外,如下:
services.AddSingleton<IMySingletonService, MySingletonService>();
2、将实例直接注入到容器中:
services.AddSingleton<IMySingletonService>(new MySingletonService());
3、工厂的方式注册服务:
services.AddSingleton<IMySingletonService>(serviceProvider => {
// 因为这里我们可以用 serviceProvider,所以我们通过这个从容器里面获取多个对象,然后进行组装,得到我们最终的实现实例
// 即工厂可以设计得比较复杂,如:实现类依赖了我们容器里的另外一个类的情况,或者我们期望用另一个类来包装我原有的实现的时候就可以使用
return new MySingletonService();
})
尝试注册服务(即:服务如果注册过了,就不会再注册了)
TryAddSingleton
服务的接口类型如果已被注册过了,那么就无法再注册了;
services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonServiceEx>(); // 尝试注册,这里无法注册,因为使用 TryAddSingleton ,即:服务的接口类型已被注册
// 注意,这里看的是 接口 是否重复注册,而不是看实现类是否重复
// 在控制器中:
public int GetServiceList([FromServices]IEnumerable<IMySingletonService> services){ // 获取注册过的所有IMySingletonService的对象
foreach(var item in services){
Console.WriteLine($"获取到服务实例:{item.ToString()}:{item.GetHashCode()}");
}
return 1;
}
发现只有一个:
TryAddEnumerable
服务的接口类型即使相同,而实现类不同,那么这个服务同样是能被注册的
services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>()); // 尝试注册,可以注册,使用 TryAddEnumerable 后,即使服务的接口类型相同,但是其实现类不同,也能被注册;
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonServiceEx>()); // 无法注册
// 注意,这里看的是 实现类 是否重复注册,而不是看服务的接口类型是否重复
移除和替换 注册
services.RemoveAll<IMySingletonService>();
services.Replace(ServiceDescriptor.Singleton<IMySingletonService, MySingletonServiceEx>());
泛型模板 注册
当我们需要注册一组泛型实现的时候,我们实际上注册的时候是不知道我们的泛型类的具体的类型入参是什么的,依赖注入框架为我们提供了泛型模板的注册方式,就意味着我们可以把泛型模板注册进去,通过一行代码来注册所有的泛型的具体的实现;
public interface IGenericService<T>{}
public class GenericService<T>: IGenericService<T>{
publiic Data {get; private set;}
public GenericService(T data){
this.Data = data;
}
}
// 注册泛型模板:
services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>));
// 控制器中的使用
public MyApiController(IOrderService orderService, IGenericService<IOrderService> genericSerivce){
// ...
}
完