.NET8 学习笔记(二)

361 阅读6分钟

.NET8专题(二)

学习目标

  1. 第一个ASPNETCore应用
  2. ASP.NETCore启动流程
  3. 依赖注入与控制反转
  4. 依赖注入基本用法
  5. 生命周期管理

Web方向介绍

  1. ASP.NET Core = MVC/WebApi/Blazor
  2. Asp.NET Core:web开发平台,signalR,grpc,blazor

项目结构

  1. launchSetting.json:启动配置文件,

  2. appsettings.json:当前应用配置文件

  3. 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();
    
  4. .NET8 应用程序一般架构

    • 应用程序+内置服务器:构成真正的.NET应用程序
    • kestrel:并不是一个完整的web服务器,像Nginx,监听80端口,他会监听不同的主机头,分配给不同的应用,而kestrel会监听80端口所有的流量,并会将监听到的内容全部传递给本应用

image-20240702221301868.png

  1. httpAPi.http:终结点资源管理器,可以直接调试controller,.NET8 新增

  2. 启动程序就两步

    • 注入服务到容器
    • 使用服务
  3. 启动项目后,会出现一个控制台页面,是因为 在appsettings.json中默认配置了Logging,级别为info,来源为主机,内容为主机在干什么

  4. 如果以IIS启动应用,就不会使用kestrel了,因为IIS启动,.NET3之后程序默认是进程内托管,不会再使用kestrel了

依赖注入和控制反转

依赖注入系统提供两个核心功能

  1. 注册类型(服务):把类型注册进依赖注入系统
  2. 解析实例:类型被注入进依赖注入系统中之后,就可以被其他同样被注入依赖注入系统的类型实例所使用

控制反转(IOC容器):

  1. 所有注册的服务以及依赖注入创建的实例,都会被保存到IOC容器中;
  2. 一种反转流,依赖和接口的方式,把程序代码直接操控的对象调用权交给第三方,通过第三方实现对象组件的装配和管理
  3. 四岁儿童,饿了想吃,去冰箱,食物自己拿
  4. 四岁儿童说饿了,父母,取出冰箱食物,拿给他
  5. 对组件对象的控制权需要对象自己去new(程序代码),转变为了,需要对象,IOC容器直接拿给我(第三方容器),这种关系就是控制反转
  6. 冰箱是IOC容器,,父母是依赖注入系统(类型注册,实例解析)

依赖注入与控制反转的关系

  1. 依赖注入是实现控制反转的一种方式,因为想要实现控制反转,想要直接把对象组件拿给小孩,父母就采取了这么一种方式,先注册类型(服务),把对象组件统一交给IOC容器(把食物放冰箱),小孩想要使用对象的时候,再经过解析直接拿出来),就可以直接给小孩了。
  2. IOC容器:ServiceProvider
  3. .NET Core web应用都是基于依赖注入系统的

依赖注入的基本使用

  1. //控制台应用的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;
    
  2. 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);
        }
    }
    
    
    • 生命周期:管理服务实例,在注册实例的时候,指定生命周期,瞬时,作用域,单例
  3.     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());
        }
    }
    
  4.     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>();
        }
    }
    
  5.     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("释放根容器");
        }
    }
    
  6. 服务的生命周期:代表一个服务实例的生存期

    • 瞬时:使用过后,即刻销毁,过了方法的作用域,实例就没有了,每次从依赖注入系统获得,创建一个全新的对象,不保存
    • 单例:一个实例,从创建出来之后,一直存在,直到程序关闭,才销毁,第一次从依赖注入系统获得,才会创建一个全新的对象,保存
    • 作用域:看情况保存,一个请求就是一个作用域
  7. 服务容器:

    • 根容器:初创只有一个,单例下的实例
    • 子容器:很多个,作用域下的实例
    • 根容器内的东西,对子容器都是可见的,子容器内的东西,互相是不可见的
    • 瞬时下的实例,不会保存在任何容器中
  8. 释放

    • 释放策略,同样与生命周期有关
    • 瞬时和作用域下的实例,如果实现了IDisposable接口,会被当前子容器保存(瞬时下的实例虽然不会保存,但也跟随子容器一起被释放),只有子容器释放时,实例才会被释放
    • 单例生命周期,如果实现了IDisposable接口,只有根容器释放时,实例才会被释放