ASP.NET Core在底层内置了一个依赖注入框架,通过依赖注入的方式注册服务、提供服务。依赖注入不仅服务于ASP.NET Core自身,同时也是应用程序的服务提供者。
毫不夸张的说,ASP.NET Core通过依赖注入实现了各种服务对象的注册和创建,同时也实现了面向抽象的编程模式和编程体验,提升了应用程序的扩展性。
今天,我们普及一下ASP.NET Core中依赖注入的一些基本知识。
一、服务的注册
我们通过创建一个ASP.NET Core的项目,可以发现在Startup.cs 类中,有一个方法ConfigureServices,这个方法的注释是这样的:
This method gets called by the runtime. Use this method to add services to the container.
在ConfigureServices方法中我们可以将通过ASP.NET Core内置的依赖注入框架实现服务的的注册。
这个方法有个参数:IServiceCollection,见名知意,服务集合。
ASP.NET Core内置的依赖注入框架将服务注册信息存储到一个实现了IServiceCollection接口的对象中。默认情况下这个接口的实现类是ServiceCollection,以下是这个类的说明:
https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollection?view=dotnet-plat-ext-3.1
通过这个接口和类实现,我们可以发现,注册服务其实就是将一个服务的ServiceDescriptor对象添加到ServiceCollection集合中。
例如:
public void ConfigureServices(IServiceCollection services)
{services.Add(new ServiceDescriptor(typeof(IUserRepository), new UserRepository()));services.AddControllers();
}
ServiceDescriptor可以理解为对某个服务注册项的描述。ASP.NET Core的依赖注入容器IServiceProvider通过ServiceDescriptor的信息,动态创建服务的实例Instance.
我们看一下这个ServiceDescriptor类:
有几个关键的属性:
1. ServiceType:服务的类型,例如服务接口的类型信息
2. ImplementationType:服务的实现类型,例如服务接口实现类的类型信息
3. ImplementationInstance:实现服务的实例,一般是服务单例模式场景下使用。https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor.-ctor?view=dotnet-plat-ext-3.1#Microsoft_Extensions_DependencyInjection_ServiceDescriptor__ctor_System_Type_System_Object_
4. Lifetime:服务生命周期:Scoped(同一个请求中同一个IServiceProvider提供的对象是同一个)、Singleton(单例)、Transient(每次从服务容器进行请求时创建)
5. ImplementationFactory 服务实例创建工厂,自定义的IServiceProvider服务提供容器
服务注册提供了一系列重载的方法,大家可以根据需要进行选择:
服务注册的过程中,涉及到了服务的生命周期的概念,接下来我们详细看一下。
二、服务生命周期
服务的生命周期设置,决定了服务提供容器IServiceProvider使用什么样的方式提供服务实例对象。正如上面第一章节所说的,
ASP.NET Core服务依赖注入框架,支持三种类型的服务生命周期:
Singleton
Scoped
Transient
其中:
Transient:暂时的,每次从服务容器进行请求时创建。 这种生存期适合轻量级、 无状态的服务。
Singleton:单一实例,在第一次请求时(或者在运行 Startup.ConfigureServices 并且使用服务注册指定实例时)创建的。每个后续请求都使用相同的实例。
Scoped:范围内的,作用域生存期服务,以每个客户端请求(连接)一次的方式创建。可以这么理解:同一个请求中同一个IServiceProvider提供的对象是同一个。
微软给了个例子不错:先注册服务,三种类型
public void ConfigureServices(IServiceCollection services)
{ <br> services.AddRazorPages(); <br> services.AddScoped<IMyDependency, MyDependency>();services.AddTransient<IOperationTransient, Operation>();services.AddScoped<IOperationScoped, Operation>();services.AddSingleton<IOperationSingleton, Operation>();services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));//OperationService depends on each of the other Operation types.services.AddTransient<OperationService, OperationService>();}
第一个请求:
控制器操作:暂时性:d233e165-f417-469b-a866-1cf1935d2518作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9实例:00000000-0000-0000-0000-000000000000OperationService 操作:暂时性:c6b049eb-1318-4e31-90f1-eb2dd849ff64作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9实例:00000000-0000-0000-0000-000000000000
第二个请求:
第二个请求:控制器操作:暂时性:b63bd538-0a37-4ff1-90ba-081c5138dda0作用域:31e820c5-4834-4d22-83fc-a60118acb9f4单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9实例:00000000-0000-0000-0000-000000000000OperationService 操作:暂时性:c4cbacb8-36a2-436d-81c8-8c1b78808aaf作用域:31e820c5-4834-4d22-83fc-a60118acb9f4单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9实例:00000000-0000-0000-0000-000000000000
大家可以根据实际的需要选择服务的生命周期,创建不同类型的服务。
三、服务的消费
前面,我们将服务注册到IServiceCollection,ASP.NET Core服务提供容器IServiceProvider就可以根据IServiceCollection 创建具体类型的服务对象了。
我们先看一下IServiceProvider接口,可以发现:只有一个GetService方法。
我们可以通过以下代码使用:
public static void Main(string[] args)
{var builder = CreateHostBuilder(args);var host = builder.Build();var userRepo = host.Services.GetService(typeof(IUserRepository)) as IUserRepository;userRepo.AddUser("user");host.Run();}
同时,我们更多常用的是:
将服务通过ASP.NET Core依赖注入框架注入到控制器中
ASP.NET Core MVC 控制器通过构造函数显式请求依赖关系。即:通过构造函数注入服务的实现。
前面,我们通过ConfigureServices注册了服务IUserRepository,在Controller这一层如何消费使用这个服务呢?答案就是在Controller构造函数中注入。
看一段示例代码:(HomeController的构造函数中,增加了一个参数IUserRepository)
public class HomeController : Controller
{private readonly ILogger<HomeController> _logger;private IUserRepository _userRepository;public HomeController(ILogger<HomeController> logger, IUserRepository userRepository){_logger = logger;_userRepository = userRepository;}public IActionResult Index(){_userRepository.AddUser(new User() { });return View();}public IActionResult Privacy(){return View();}[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]public IActionResult Error(){return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });}}
同时,ASP.NET Core MVC 控制器支持通过注解FromServicesAttribute, 将服务直接注入到Action方法中,而无需使用构造函数注入:
public IActionResult Index([FromServices] IUserRepository userRepository)
{userRepository.AddUser(new User() { });return View();
}
ASP.NET Core除了支持将服务注入到控制器,同时还支持将服务依赖注入到视图,可以参考以下链接:
https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-3.0
以上是对ASP.NET Core依赖注入框架的研究,分享给大家。
往期精彩回顾
【.net core】电商平台升级之微服务架构应用实战
.Net Core微服务架构技术栈的那些事
Asp.Net Core 中IdentityServer4 授权中心之应用实战
Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式
Asp.Net Core 中IdentityServer4 授权流程及刷新Token
Asp.Net Core 中IdentityServer4 实战之 Claim详解
Asp.Net Core 中IdentityServer4 实战之角色授权详解
Asp.Net Core 中间件应用实战中你不知道的那些事
Asp.Net Core Filter 深入浅出的那些事-AOP
Asp.Net Core EndPoint 终结点路由工作原理解读
ASP.NET CORE 内置的IOC解读及使用