.NET8专题(二)
学习目标
- 第一个ASPNETCore应用
- ASP.NETCore启动流程
- 依赖注入与控制反转
- 依赖注入基本用法
- 生命周期管理
Web方向介绍
- ASP.NET Core = MVC/WebApi/Blazor
- Asp.NET Core:web开发平台,signalR,grpc,blazor
项目结构
-
launchSetting.json:启动配置文件,
-
appsettings.json:当前应用配置文件
-
program.cs:启动类
//构建web应用程序WebApplication主机对象,是有main函数的,只不过使用了顶级语句,省略掉了 //主机Host builder:负责应用程序启动和生存周期管理,使用简化的方式来配置应用程序的请求处理管道,服务和路由 var builder = WebApplication.CreateBuilder(args); //服务的集合,用于注册和配置应用所需要的服务 //添加了controller服务,应用程序就可以接收到各种请求,并对请求做出处理了 builder.Services.AddControllers(); //添加终端结点服务,minimalAPI相关 builder.Services.AddEndpointsApiExplorer(); //添加swagger生成器服务 builder.Services.AddSwaggerGen(); //此句以上是用于构建应用程序的代码,app是实际运行的应用程序实例 var app = builder.Build(); //下面是关于管道,中间件的代码 //.NET应用程序的核心就是管道和中间件在起作用 //写的各种业务逻辑,其本质就是各种中间件 if (app.Environment.IsDevelopment()) { //使用swagger app.UseSwagger(); //swaggerUI页面 app.UseSwaggerUI(); } //强制使用https,如果不是https请求,将会被强制转换成https请求 app.UseHttpsRedirection(); //使用权限服务 app.UseAuthorization(); //配置路由匹配规则 app.MapControllers(); //终端中间件,短路,遇见该中间件就原路返回 app.Run(); -
.NET8 应用程序一般架构
- 应用程序+内置服务器:构成真正的.NET应用程序
- kestrel:并不是一个完整的web服务器,像Nginx,监听80端口,他会监听不同的主机头,分配给不同的应用,而kestrel会监听80端口所有的流量,并会将监听到的内容全部传递给本应用
-
httpAPi.http:终结点资源管理器,可以直接调试controller,.NET8 新增
-
启动程序就两步
- 注入服务到容器
- 使用服务
-
启动项目后,会出现一个控制台页面,是因为 在appsettings.json中默认配置了Logging,级别为info,来源为主机,内容为主机在干什么
-
如果以IIS启动应用,就不会使用kestrel了,因为IIS启动,.NET3之后程序默认是进程内托管,不会再使用kestrel了
依赖注入和控制反转
依赖注入系统提供两个核心功能:
- 注册类型(服务):把类型注册进依赖注入系统
- 解析实例:类型被注入进依赖注入系统中之后,就可以被其他同样被注入依赖注入系统的类型实例所使用
控制反转(IOC容器):
- 所有注册的服务以及依赖注入创建的实例,都会被保存到IOC容器中;
- 一种反转流,依赖和接口的方式,把程序代码直接操控的对象调用权交给第三方,通过第三方实现对象组件的装配和管理
- 四岁儿童,饿了想吃,去冰箱,食物自己拿
- 四岁儿童说饿了,父母,取出冰箱食物,拿给他
- 对组件对象的控制权,由需要对象自己去new(程序代码),转变为了,需要对象,IOC容器直接拿给我(第三方容器),这种关系就是控制反转
- 冰箱是IOC容器,,父母是依赖注入系统(类型注册,实例解析)
依赖注入与控制反转的关系
- 依赖注入是实现控制反转的一种方式,因为想要实现控制反转,想要直接把对象组件拿给小孩,父母就采取了这么一种方式,先注册类型(服务),把对象组件统一交给IOC容器(把食物放冰箱),小孩想要使用对象的时候,再经过解析(直接拿出来),就可以直接给小孩了。
- IOC容器:ServiceProvider
- .NET Core web应用都是基于依赖注入系统的
依赖注入的基本使用
-
//控制台应用的sdk是不包含.NET Core.Sdk框架的,因此,需修改为Web,这样sdk中就包含依赖注入系统了 <Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project> public interface IAccount; public interface IMessage; public interface ITool; -
public static class Sample01 { public class Account: IAccount{} public class Message: IMessage{} public class Tool: ITool{} public static void Run() { //ServiceCollection 服务集合,是一个List集合, //通过服务集合,能够创建出服务容器 ServiceProvider var provider = new ServiceCollection() .AddTransient<IAccount, Account>()//向服务集合中加入瞬时的服务实例 .AddScoped<IMessage, Message>()//向服务集合中加入作用域周期的服务实例 .AddSingleton<ITool, Tool>()//向服务集合中加入单例周期的服务实例 .BuildServiceProvider(); //通过GetService 获取这个服务的实例 Debug.Assert(provider.GetService<IAccount>() is Account); Debug.Assert(provider.GetService<IMessage>() is Message); Debug.Assert(provider.GetService<ITool>() is Tool); } }- 生命周期:管理服务实例,在注册实例的时候,指定生命周期,瞬时,作用域,单例
-
public abstract class Base { protected Base() { Console.WriteLine($"{GetType().Name} 已创建"); } } public class Account:Base, IAccount{} public class Message:Base, IMessage{} public class Tool:Base, ITool{} public static void Run() { //创建出服务容器之后,获取全部服务集合 var services = new ServiceCollection() .AddTransient<Base, Account>() .AddTransient<Base, Message>() .AddTransient<Base, Tool>() .BuildServiceProvider() .GetServices<Base>().ToList(); Debug.Assert(services.OfType<Account>().Any()); Debug.Assert(services.OfType<Message>().Any()); Debug.Assert(services.OfType<Tool>().Any()); } } -
public class Base { protected Base() { Console.WriteLine($"Created:{GetType().Name}"); } } public class Account: Base, IAccount{} public class Message:Base, IMessage{} public class Tool:Base, ITool{} public static void Run() { var root = new ServiceCollection() .AddTransient<IAccount, Account>() .AddScoped<IMessage, Message>() .AddSingleton<ITool, Tool>() .BuildServiceProvider(); var child1 = root.CreateScope().ServiceProvider; var child2 = root.CreateScope().ServiceProvider; GetService<IAccount>(child1); GetService<IMessage>(child1); GetService<ITool>(child1); Console.WriteLine(); GetService<IAccount>(child2); GetService<IMessage>(child2); GetService<ITool>(child2); } private static void GetService<T>(IServiceProvider provider) { provider.GetService<T>(); provider.GetService<T>(); } } -
public class Base : IDisposable { protected Base() { Console.WriteLine($"Created:{GetType().Name}"); } public void Dispose() { Console.WriteLine($"Disposed: {GetType().Name}"); } } public class Account: Base, IAccount{} public class Message:Base, IMessage{} public class Tool:Base, ITool{} public static void Run() { using var root = new ServiceCollection() .AddTransient<IAccount, Account>() .AddScoped<IMessage, Message>() .AddSingleton<ITool, Tool>() .BuildServiceProvider(); using (var scope = root.CreateScope()) { var child = scope.ServiceProvider; child.GetService<IAccount>(); child.GetService<IMessage>(); child.GetService<ITool>(); Console.WriteLine("释放子容器"); } Console.WriteLine("释放根容器"); } } -
服务的生命周期:代表一个服务实例的生存期
- 瞬时:使用过后,即刻销毁,过了方法的作用域,实例就没有了,每次从依赖注入系统获得,创建一个全新的对象,不保存
- 单例:一个实例,从创建出来之后,一直存在,直到程序关闭,才销毁,第一次从依赖注入系统获得,才会创建一个全新的对象,保存
- 作用域:看情况保存,一个请求就是一个作用域
-
服务容器:
- 根容器:初创只有一个,单例下的实例
- 子容器:很多个,作用域下的实例
- 根容器内的东西,对子容器都是可见的,子容器内的东西,互相是不可见的
- 瞬时下的实例,不会保存在任何容器中
-
释放
- 释放策略,同样与生命周期有关
- 瞬时和作用域下的实例,如果实现了IDisposable接口,会被当前子容器保存(瞬时下的实例虽然不会保存,但也跟随子容器一起被释放),只有子容器释放时,实例才会被释放
- 单例生命周期,如果实现了IDisposable接口,只有根容器释放时,实例才会被释放