一、什么是依赖注入
依赖注入是一种软件设计模式,用于解决一组组件之间的依赖关系。它的目的是实现组件之间的解耦,使得组件更加可复用和可测试。
在依赖注入中,一个组件(称为依赖)不会直接创建或管理它所需要的其他组件,而是通过外部的一个容器(或框架)来注入它所需要的依赖。
二、依赖注入的简单实现
下面创建的是一会需要实现的接口和方法。
public interface IName
{
public void Name(string NewName);
}
public class MyName : IName
{
public void Name(string NewName)
{
Console.WriteLine($"我叫{NewName},这是MyName");
}
}
public class HisName : IName
{
public void Name(string NewName)
{
Console.WriteLine($"他叫{NewName},这是HisName");
}
}
public class GetName
{
private IName _IName;
public GetName(IName iName)
{
_IName = iName;
}
public void NewGetName(string NewName)
{
_IName.Name(NewName);
}
}
下面我们来看一下普通方式和依赖注入有何区别。
1、普通方式实现
普通方式实现就是使用NEW关键字对方法进行创建,我们来调用GetName类中的NewGetName方法。我们可以看到两行代码就可以搞定了。
GetName getName = new GetName(new MyName());
getName.NewGetName("张三");
2、依赖注入实现
接下来就是使用依赖注入的方式来实现,实现之前我们需要安装一个NuGet 包,下面两个随便安装一个即可。
Microsoft.Extensions.Hosting
Microsoft.Extensions.DependencyInjection
安装完成后,我们看下面的实现方式,其中ServiceCollection 是一个接口,用于描述一组用于注册服务的方法。 AddScoped表示的是服务生存期,这里为作用域内。这里的GetRequiredService是从泛型中获取服务。
简单来说就是ServiceCollection开始创建,AddScoped将东西存放起来,GetRequiredService获取东西。
ServiceCollection services = new ServiceCollection();//创建服务
services.AddScoped<GetName>();
services.AddScoped<IName, MyName>();
using (ServiceProvider scope = services.BuildServiceProvider())
{
GetName getName = scope.GetRequiredService<GetName>();
getName.NewGetName("张三");
}
两种方式总结:
上面两种方式最后的实现效果都是相同的, 但是为什么不推荐使用第一种而推荐使用第二种,第二种比第一种麻烦那么多,最主要的是依赖注入能够将组件之间的依赖关系解耦,使得它们可以独立地进行开发和测试。而普通方式会导致组件之间的紧耦合,难以进行独立测试和开发。
三、服务生存期
上面我们提到了AddScoped是服务生存期,接下来我们来了解一下,服务生存期主要分为三种。
1、暂时
AddTransient暂时生存期服务是每次从服务容器进行请求时创建的,在请求结束时会释放暂时服务。
services.AddTransient<GetName>();
services.AddTransient<IName, MyName>();
2、作用域
AddScoped指定了作用域的生存期,在某个作用域内有效,出了作用域失效。
services.AddScoped<GetName>();
services.AddScoped<IName, MyName>();
3、单例
AddSingleton在首次请求它们时进行创建,或者在向容器直接提供实现实例时进行创建。在处理请求中时,当应用关闭并释放ServiceProvider,就会释放该服务,反之,应用一直存在,那么该服务就一直生效。
services.AddSingleton<GetName>();
services.AddSingleton<IName, MyName>();
四、获取服务的方法
1、GetServices(多个)
获取指定类型的所有服务实例,GetServices方法会返回一个IEnumerable<T>类型的集合,其中包含所有实现了接口的服务实例。
IEnumerable<GetName> getName = scope.GetServices<GetName>();
foreach (GetName name in getName)
{
name.NewGetName("张三");
}
2、GetService(单个)
获取指定类型的最后一个注册的服务实例,容器会检索已经注册的服务实例,并返回对应类型的实例,如果该类型的服务不存在,它会返回null。
GetName getName = scope.GetService<GetName>();
getName.NewGetName("张三");
3、GetRequiredService(单个)
GetRequiredService方法的作用是从容器中获取指定类型最后一个注册的服务实例。如果容器中不存在该类型的服务,则会抛出一个异常。
GetName getName = scope.GetRequiredService<GetName>();
getName.NewGetName("张三");
五、Web Api中使用依赖注入
1、构造器注入
首先需要到Program.cs文件中对需要使用的方法进行注册。
builder.Services.AddScoped<GetName>();
builder.Services.AddScoped<IName, MyName>();
注册完成之后,到API控制器当中创建构造器。
private readonly GetName getname;
public AddsController(GetName getname)
{
this.getname = getname;
}
[HttpPost]
public void Name()
{
getname.NewGetName("张三");
}
2、[FromServices]注入
使用构造器注入会有一个问题,就是当前这个API控制器当中如果有多个网络请求,但是只有一个网络请求会用到这个服务,那么在使用不会用到这个服务的网络请求的时候,这个服务还是会被注册。简单来说就是我不管你API控制器当中用不用到这个服务,你对我进行了创建构造器,我都会被创建注册。
所以这里推荐使用[FromServices]注入,它是你这个网络请求需要使用到这个服务,我才会创建注册。
同样的它需要到Program.cs文件中对需要使用的方法进行注册。
builder.Services.AddScoped<GetName>();
builder.Services.AddScoped<IName, MyName>();
注册完成之后到API控制器当中,现在我那个网络请求需要使用到这个服务,我就去那个网络请求那里去配置。这样我不使用这个网络请求就不会对服务创建注册。
[HttpPost]
public void Name([FromServices] GetName getname)
{
getname.NewGetName("张三");
}