# aspnetcore **Repository Path**: soul-au/aspnetcore ## Basic Information - **Project Name**: aspnetcore - **Description**: aspnetcore企业级开发课程 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 49 - **Forks**: 19 - **Created**: 2023-01-29 - **Last Updated**: 2025-09-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AspNetCore企业级开发 学习交流:307564339 讲师邮件:1448376744@qq.com ## OOP ### 封装 POP ```C #pragma once #include #include //没有访问权限控制 //数据和函数分离 //缺少限定 struct Person { int age; }; //无参构造器 Person* person_new() { return person_new(0); } //带参构造器 Person* person_new(int age) { Person* ts = (Person*)malloc(sizeof(Person)); if (ts != NULL) { ts->age = age; return ts; } return NULL; } //由于数据和函数的分离,导致必须通过前缀来进行限定 //ts即面向对象语言中的this int person_get_age(Person* ts) { return ts->age; } void person_set_age(Person* ts, int age) { ts->age = age; } void person_do_some1(Person* ts) { printf("%d", ts->age); } //静态函数本质就是全局函数,但是没有限定 void person_do_some2() { Person* ts = person_new(); if (ts != NULL) { printf("%d", ts->age); } } void test() { //实列化 Person* student = person_new(20); printf("%d\n", student->age); person_set_age(student, 20); //调用实列函数 person_do_some1(student); //调用全局函数(静态函数) person_do_some2(); } ``` OOP ```C# public class Person { /// /// 字段:用于保存数据,用来抽象事物的属性 /// private int age; /// /// 属性:用于控制字段的访问权限和逻辑 /// 注意:属性是特殊的实列函数 /// public int Age { get { //访问逻辑 return age; } set { //访问逻辑 age = value; } } /// /// 实列函数:实列相关,所有的实列函数都有this(属性也有),代之当前实列。 /// public void DoSome1() { Console.WriteLine(this.Age); } /// /// 静态函数:类型相关,所有可访问的范围都能访问。 /// 注意:静态函数无法访问实列函数,本质是因为没有this /// public static void DoSome2() { //Console.WriteLine(this.Age);//错误没有this var obj = new Person(); obj.DoSome1();//间接调用 } /// /// 事件:事件既不是字段也不是属性,就是事件,没有getter,setter /// 注意:事件的类型必须是委托 /// 站在oop的角度来看委托的本质是接口,java就是通过接口来实现委托的。 /// public event Action? OnSleep = null; public void Sleep() { //发布,触发事件 OnSleep?.Invoke(this); } /// /// 静态属性:注意区分属性和字段的区别(是否有get|set) /// public static string Name { get; } /// /// /// static Person() { Name = "Person"; } /// /// 无参构造:默认提供,如果提供了带参构造则不提供无参构造,及构造器至少有一个 /// 注意:构造器也是特殊的实列函数 /// public Person() : this(0) { } /// /// 带参构造 /// /// public Person(int age) { this.age = age; } } ``` 封装:将字段和函数封装在一个class,并且可以设置成员的访问级别。 字段:用于保存数据 属性:控制字段的get,set行为。以及访问级别 实列函数:用于描述对象的行为,属性本质也是实列函数(注意区分字段和属性)。 静态函数:用于描述类型的行为 构造函数:至少有一个,默认会提供一个无参数构造器,本质也是一个实列函数 ### 继承 ```C# public class Person { private int id; public int Id { get { return id;} } /// /// 实列函数 /// public void DoSome1() { Console.WriteLine(id); } /// /// 虚函数:虚函数可以重写 /// public virtual void DoSome2() { } /// /// 实列函数 /// public static void DoSome3() { } } /// /// 继承:子类继承父类所有代码(注意“所有”二字) /// public class Student : Person { public void Test1() { //可以理解成这三个方法都在Student里面所已可以调用 DoSome1(); DoSome2(); DoSome3(); } public static void Test2() { //可以理解成这三个方法都在Student里面所已可以调用 DoSome1();//静态函数的无法调用实列函数(可以理解成自己的实列函数) DoSome3(); } public void Test3() { //访问不了不代表没有继承。试想子类中没有id字段,那么Id属性拿到的是谁的值? Console.WriteLine(base.id);//无法直接访问父类中的私有成员 Console.WriteLine(Id);//可以通过公开的方法或者属性来间接访问 } /// /// 重写:子类可以重写父类的虚函数 /// public override void DoSome2() { //base是代词,代指父类的实列 base.DoSome2();//显示调用父类中的DoSome2 //this是代词,代指当前的实列 this.DoSome1(); } /// /// 覆盖:子类可以覆盖父类中的实列函数 /// public new void DoSome1() { } } ``` 继承:子类继承父类的所有代码,因此你可以吧两个类想象成是一个类。 超类:object类型,所有类都派生自object。 传递:继承具有传递性。 重写:子类重写父类的虚函数。 覆盖:子类重写父类的同名函数。 静态:静态成员是类相关的,全部继承过来。 访问:子类可以自己访问父类的公共成员,间接访问私有成员。 base:子类可以通过base来显示调用父类的成员。 ### 多态 ```C# public abstract class Animal { public virtual void Say() { Console.WriteLine("呜呜呜~~~"); } } public class Dog : Animal { public override void Say() { Console.WriteLine("汪汪汪~~~"); } } public class Cat : Animal { public override void Say() { Console.WriteLine("喵喵喵~~~"); } } public class Program { public static void Main(string[] args) { //object是所有类的超类(超类就是基类的基类) object animal2 = new Dog(); animal2.Say();//编译时无法通过 //可以进行转换 var dog1 = animal2 as Dog; dog1?.Say(); var dog2 = animal2 as Animal; dog2?.Say(); //转换失败 var cat = animal2 as Cat; cat?.Say(); //多态:父类型的引用可以指向子类型的对象 Animal animal1 = new Dog(); //编译时:函数与引用的类型绑定 //运行时:函数与实列的类型绑定 //编译时会检查Animal是否有Say函数,运行时执行实列的函数 animal1.Say(); } } ``` 多态:父类型的引用指向子类型的对象,编译时与引用类型绑定,运行时与实列类型绑定。 ### 实例化 方式1:new(最优) 方式2:泛型(和new差不多,.net是真泛型) 方式3:反射(Activator) 方式4:表达式树(Expression,性能比反射好,需要缓存,不如emit灵活,不支持语句) 方式5:Emit(DynamicMethod,性能比反射好,需要缓存,比较灵活,支持语句) 思考:如何缓冲Emit的委托?带有构造器的委托? 编写一个类用于记录要实列化的类型,以及构造器信息,参数列表信息。放入一个线程安全的字典里。注意要重写hashcode和equals。 ### 相等性 ==:默认比较的是引用的地址 equals:默认(object)比较的是引用的地址 重载==:要同时重载!=,一般要重写equals并且同时重写hashcode,一般通过equals来重载== 重写equals:先比较引用,在比较是否为null,在强制转换,比较属性。 ### 哈希表 ![hashtable](./Imgs/hashtable.png) 数据结构:哈希表中的元素不能重复,哈希表的特点是查询和增删性能都比较时候。哈希表大概是由一个数组和链表构成。把数组称之为桶。加上桶长为4如同。整个表一共有4个元素。哈希表也称之为散列表,哈希码也称之为散列码。 假设这个哈希表有4个桶,容量为5 第一步:获取10的hashcode,即152。 第二步:散列,公式为哈希码%桶长,即152%4=0 第三步:定位hash桶,即桶1(下标从0开始) 第四步:取出桶1的链表,迭代桶1中的链表,将10与链表的每一个元素进行equals比较,如果全为false就插入,否则就不插入。 第五步:如果第四步插入的时候发现元素的个数超出了哈希表的容量,此时要进行rehash。rehash的过程非常耗时,需要创建一个新的桶,把旧的桶的元素复制到新的桶里面。注意复制是需要时间的,建议使用hash桶设置一个合理的容量。 思考1:为什么重写equals要同时重新hashcode? 思考2:桶长变了,元素的hashcode不变,假设桶长变为16,如何保证之前的元素10还在1号桶里? 思考3:为什么需要提前预算hash表的容量? 思考4:假设有一个分布式系统,如何进行分流,根据什么进行hash?(一般用cookie,因为考虑到用户没有登入的情况) ### 逆变和协变 协变:对于泛型接口,使得泛型的完整类型满足多态,进行妥协(必须是泛型接口) 逆变:对于泛型接口,使得泛型的完整类型满足反态,进行叛逆(必须是泛型接口) ```C# public class Person { } public class Student : Person { } public interface IPerson1 { } /// /// 逆变 /// /// public interface IPerson2 { } /// /// 协变 /// /// public interface IPerson3 { } public void Test1() { //泛型是不完整的类型,指定泛型参数之后才完整 IPerson1? p1 = null; IPerson1? p2 = null; //IPerson1和IPerson1不存在继承关系 //p1 = p2; } public void Test2() { //泛型是不完整的类型,指定泛型参数之后才完整 IPerson2? p1 = null; IPerson2? p2 = null; //IPerson1和IPerson1不存在继承关系,但是支持逆变 p2 = p1; } public void Test3() { //泛型是不完整的类型,指定泛型参数之后才完整 IPerson3? p1 = null; IPerson3? p2 = null; //IPerson1和IPerson1不存在继承关系,但是支持协变 p1 = p2; } public void Test4() { //协变的应用 IEnumerable? p1 = null; IEnumerable? p2 = null; p1 = p2; } ``` ### 集合 #### IEnumerable接口 1. 都可以foreach,表示可迭代的 2. 支持linq中 3. 支持yield便捷实现IEnumerable接口 4. 所有返回IEnumerable的方法不会立即执行 #### IEnumerable实现 方式1:yeild ```C# public IEnumerable GetRandomNumberList(int count) { for (int i = 0; i < count; i++) { yield return Random.Shared.Next();//使用yield编译器会给你取实现IEnumerable接口 } } ``` 方式2:class ```C# public class RandomNumber : IEnumerable { private int[] list; public RandomNumber(int count) { list = new int[count]; for (int i = 0; i < count; i++) { var item = Random.Shared.Next(); list[i] = item; } } public IEnumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } class Enumerator : IEnumerator { private readonly RandomNumber obj; private int _index = -1; public Enumerator(RandomNumber obj) { this.obj = obj; } public int Current => obj.list[_index]; object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { //迭代器一般用内部类 //内部内可以访问到外部类的私有成员 if (_index < obj.list.Length) { _index++; return true; } return false; } public void Reset() { _index = -1; } } } ``` ## IOC ### 基础知识 我们如何理解IOC?我们可以通过一个现实世界的模型来进行解释。比如有一本菜谱这个菜谱就是我们的IServiceCollection,里面记录了菜名(ServiceType)以及菜具体制作(ImplementationType),通过菜名(ServiceType)告诉厨师(IServiceProvider)制作(实列化、解析)出来我们要吃的菜。 - 依赖项 Microsoft.Extensions.DependencyInjection.Abstractions:抽象包,用于扩展容器 Microsoft.Extensions.DependencyInjection:实现包,实现IOC的基本功能 - 核心接口 ServiceDescriptor:用于描述服务的信息。比如服务名、实现类、生命周期。(菜单) IServiceCollection:是一个List,用于保存ServiceDescriptor元素。 IServiceProvider:用于解析服务实列 ActivatorUtilities:用于解析一个容器中不存在,但是依赖了容器中的服务的实列。 - 关键字 依赖:如果一个类A的构造器中有一个类B的参数,我们说A依赖B 注入:如果A依赖B,要想实列化A,就必须先实列化B,然后把B载入A的构造器的过程 依赖注入:IOC容器根据反射得到一个类的依赖关系,自动帮你载入依赖项的过程 ```C# public class IConnection { } public class SqlConnection : IConnection { } public class DbContext { //DbContext依赖IConnection public DbContext(IConnection connection) { } } IConnection connection = new SqlConnectioon(); //注入:手动注入,容器可以自动注入 var context = new DbContext(connection); ``` ### 服务注册 ```C# //创建容器 IServiceCollection services = new ServiceCollection(); //1.通过ServiceDescriptor创建,写框架时有用(万能公式) services.Add(new ServiceDescriptor(typeof(IConnection),typeof(SqlConnection),ServiceLifetime.Singleton)); //2.泛型方式,此时服务类型为IConnection services.AddSingleton(); //3.委托注册,可以定义创建逻辑,在注册服务时也能解析服务 services.AddSingleton(sp => { //sp:是容器实列可以用于解析以注册的服务(IServiceProvider) var connection = sp.GetService(); return new DbContext(connection,"fff"); //高级 //return ActivatorUtilities.CreateInstance(sp,"fff"); }); //4.服务类型和实现类型相同 services.AddSingleton(); //5.泛型注册,这样可以获取到所有Logger<>的完整类型(泛型参数不要写死) services.AddSingleton(typeof(Logger<>)); //6.反射的方式,写框架很有用 services.AddSingleton(typeof(SqlConnection)); //7.注意使用反射构建泛型参数,此时注册的是Logger服务(ps:写框架的人会用到) services.AddSingleton(typeof(Logger<>).MakeGenericType(typeof(Program))); //8.替换服务,如果服务已注册则不进行注册。一般写框架会用到,如果框架使用了Try...那么你可以使用自定义的服务在它之前进行替换 services.TryAddSingleton(typeof(SqlConnection)); ``` ### 设计模式 设计模式就是解决特定问题的套路,使用设计模式可以方便沟通、理解和相互学习。不要把设计模式学死了。 #### 工厂模式-(侧重对象管理) 1.工厂模式主要用于实现对象的创建,多实例的管理,命名对象的管理,也可以用于管理Provider。和Manager模式的区别是,工厂模式一般不负责执行业务。 2.由于微软的容器只能更加类型来解析服务,有时候我们需要通过名称来解析服务,此时需要使用工厂模式。 3.命名模式的支持,比如ILoggerFactory ```C# public class Connection { } public class ConnectionFactory { private IServiceProvider _serviceProvider; private Dictionary _connections; public ConnectionFactory(IServiceProvider provider, Dictionary connections) { _serviceProvider = provider; _connections = connections; } public Connection? Get(string name) { if (_connections.TryGetValue(name, out Type? connectionType)) { return _serviceProvider.GetService(connectionType) as Connection; } return default; } } ``` #### 构造者模式-(侧重对象构建) 1.通过一个构造器(Builder)来提供丰富的api来构造目标对象。简化目标对象的创建,丰富目标对象的创建方式。构造器一般要提供一个Build用来返回被构造的对象的实列。 2.IServiceCollection:就是IServiceProvider的构造者 3.注意区分构造函数 ```C# public class Connection { } public class ConnectionFactoryBuilder { private Dictionary _connections = new(); public ConnectionFactoryBuilder Add(string name,Connection connection) { _connections.Add(name, connection); return this;//一般要支持链式调用 } public ConnectionFactory Build() { return new ConnectionFactory(_connections); } } public static class ConnectionFactoryBuilderExtensions { public static ConnectionFactoryBuilder Add(this ConnectionFactoryBuilder builder, Connection connection) { var name = connection.GetType().Name; builder.Add(name, connection); return this;//一般要支持链式调用 } } ``` #### 提供者模式-(侧重业务) 1.提供者模式一般支持用户实现,并且支持多实现的。比如日志有控制台提供程序,Debug提供程序,自定义提供程序。 2.提供者更加倾向于业务,一般提供者都是设计成可以支持用户去实现,支持多种扩展的。 3.提供者模式和工厂模式相识,一般我们希望用户可以自定义并且支持多实现的时候使用提供者模式。 4.Provider和工厂模式的区别是,Provider更加倾向于业务逻辑的封装。 ```C# public interface IConfigurationProvider { string Get(string key); } public class JsonConfigurationProvider: IConfigurationProvider { } public class XmlConfigurationProvider : IConfigurationProvider { } ``` #### 管理者模式-(侧重业务管理) 1.用于管理模式,当我们有多个策略需要一个管理者来管理的时候,可以使用Manager模式。管理者模式可以用于管理Provider。 2.管理者模式和工厂模式也很像,但是管理者模式除了管理对象,还负责执行业务。 ```C# public class ConfigurationManager { private List _providers = new (); public void AddProvider(IConfigurationProvider provider) { _providers.Add(provider); } public string Get(string key) { foreach(var item in _providers) { var value = item.Get(key); if(value != null) { return value; } } return default; } } ``` ### 基本使用 ```C# IServiceCollection services = new ServiceCollection(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); IServiceProvider container = services.BuildServiceProvider(); var connection = container.GetRequiredService(); var service = container.GetService(); Console.WriteLine(connection.GetType().Name); ``` ### 服务解析 ```C# //创建容器 IServiceCollection services = new ServiceCollection(); //注册服务 services.AddSingleton(); //构建容器 IServiceProvider container = services.BuildServiceProvider(); //解析服务 var connection = container.GetService(); //解析服务,如果解析不到会抛出异常 var connection = container.GetRequiredService(); //解析所有IConnection类型的服务 IEnumerable connections = container.GetServices(); //解析一个没有注册到容器但是依赖了容器已注册的服务,写框架常用 ActivatorUtilities.CreateInstance(container); ``` ### 生命周期 根容器:生命周期与应用程序一致。 子容器:声明周期由开发者决定。 Singleton:同一个容器无论是否是根容器解析出来的实列都是唯一的。 Transient:每次解析都是一个新的实列 Scoped:同一个IServiceScope解析出来的实列是唯一的。 Scoped要点: 1.不要通过根容器来解析Scope实例的服务,因为根容器在程序运行过程中不会释放。那么解析出来的服务也不会释放。 2.Scope的范围有多大不是却决于一次http请求,而是却决于你何时释放。 3.IServiceScope会记录下由它解析出来的服务,如果IServiceScope实列被释放,那么由它解析出来的实列都将被释放(单实列除外)。 4.注意虽然根容器和子容器都实现了IServiceProvider接口,但是他们的实现类不一样。 5.单实列的服务不要去依赖一个Scope级别的服务。 搭建测试案例 ```C# public class Connection { public string Id { get; } //每次实列化的时候执行一次,得到一个唯一id public Connection() { Id = Guid.NewGuid().ToString(); } } var services = new ServiceCollection(); services.AddScoped(); var container = services.BuildServiceProvider(new ServiceProviderOptions() { ValidateScopes = true,//指示是否可以通过根容器来解析Scope实列。 ValidateOnBuild = true//构建之前时检查是否有依赖没有注册的服务 }); //测试scoped var connection1 = container.GetRequiredService(); Console.WriteLine(connection1.Id); using(var scope = container.CreateScope()) { var connection2 = scope.ServiceProvider.GetRequiredService(); Console.WriteLine(connection2.Id); } ``` ### 组件扫描 组件扫描可以通过接口或者特性的方式。这里我们展示使用特性的方式,因为特性可以配置参数。 ```C# //定义一个注解 [AttributeUsage(AttributeTargets.Class)] public class InjectionAttribute : Attribute { public Type? ServiceType { get; set; } public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient; } public static class DependencyInjectionExtensions { //扫描 public static IServiceCollection AddInjectionServices(this IServiceCollection services) { var serviceTypes = GetInjectionServiceTypeList(typeof(T).Assembly); foreach (var item in serviceTypes) { var injection = item.GetCustomAttribute(); if (injection!.ServiceType == null) { services.Add(new ServiceDescriptor(item, item, injection.Lifetime)); } else { services.Add(new ServiceDescriptor(injection!.ServiceType, item, injection.Lifetime)); } } return services; } private static IEnumerable GetInjectionServiceTypeList(Assembly assembly) { var serviceType = assembly.GetTypes() .Where(a => a.IsClass) .Where(a => a.GetCustomAttribute() != null) .Where(a => !a.IsAbstract); return serviceType; } } ``` ```C# public interface ILogger { void Log(); } //注入 [Injection(ServiceType = typeof(ILogger<>), Lifetime = ServiceLifetime.Singleton)] internal class Logger: ILogger { public void Log() { Console.WriteLine(typeof(T).Name+":success!"); } } public static void TestScanner() { var services = new ServiceCollection(); //扫描组件 services.AddInjectionServices(); var container = services.BuildServiceProvider(); var logger = container.GetRequiredService>(); logger.Log(); } ``` ### 基本原理 自定义IOC需要实现一下两步: 1.编写一个ContailerBuilder,用于注册服务的描述信息,并且能够兼容IServiceCollection注册的服务描述。 2.实现IServiceProvider接口,通过加载ContailerBuilder,并解析服务。 思考为什么是这两步? 1.因为微软的大部分组件都是基于IServiceProvider来进行服务解析的。因此这个接口必须实现。 2.ServiceCollection注册服务的描述信息很简单。你需要更加复杂的容器实现,因此ServiceDescriptor无法描述你的服务类型。因此你需要写一个ContailerBuilder用于记录你的服务的描述信息。 3.需要兼容IServiceCollection注册的服务的描述。比如autofac支持属性注册,但是通过ServiceDescriptor无法描述。因为很多框架的服务注册是基于IServiceCollection,因此你必须能兼容微软的IOC的全部能力。 原理 我们需要一个ContainerBuilder和一个Container类和服务描述类ServiceDescriptor。ContainerBuilder本质是一个集合,用于记录用户注册的服务组件,以及描述信息。 ServiceDescriptor:服务描述信息(服务类型、实列类型,生命周期,创建委托等等),告诉Container将来如何解析实列化服务。 ContainerBuilder:用于记录描述信息的集合,提供api快速便捷的构建容器。 Container:用于解析服务,创建实列。 注册过程:就是创建服务描述的过程,向ContainerBuilder添加服务描述,比如告诉容器这个服务的生命周期,服务类型,实现类型,创建方式,是否支持属性注入,配置实例化时的回调,释放时的回调等等。 构建过程:创建容器的过程,完成容器的一些初始化,并讲服务注册的描述信息传递给容器。 解析过程:一般通过服务的类型,我们可以理解为它是服务的key,通过服务的key找到服务注册的描述信息。 如果是普通注册的服务,那么解析这个实列的时候,找到这个实列的构造器,得到这个实列依赖的其他服务,创建依赖的实列,这是一个递归的过程。 如果是委托注册的服务,那么解析这个实列的时候,调用委托返回实列。 如果是命名注册的服务,那么一般是通过一个工厂模式来解析。 实列化的方式可以参考OOP中的几种实列化方式,反射,表达式树,Emit等技术。 ### Autofac [autofac/Autofac.Extensions.DependencyInjection:Microsoft.Extensions.DependencyInjection.Abstractions(.NET Core dependency injection abstraction)中接口的Autofac实现。 (github.com)](https://github.com/autofac/Autofac.Extensions.DependencyInjection) autofac提供了更多的功能,比如属性注入,组件扫描等等非常丰富的功能。我也很少使用。微软的IOC容器只支持构造函数的依赖关系注入但是基本够用,如果还要其它需求的可以选择使用autofac。 ```C# public static void TestAutofac() { var services = new ServiceCollection(); //微软的容器注册服务 services.AddScoped(typeof(ILogger<>),typeof(Logger<>)); var builder = new ContainerBuilder(); //autofac容器注册服务 builder.RegisterType().PropertiesAutowired() .As() .InstancePerLifetimeScope(); builder.Populate(services);//将IServiceCollection中的服务注册到autofac //使用AutofacServiceProvider的实现方案,创建容器 //加载autofac中的服务 IServiceProvider container = new AutofacServiceProvider(builder.Build()); var logger = container.GetRequiredService>(); var service = container.GetRequiredService(); } ``` ## Configuration ### package说明 Microsoft.Extensions.Configuration.Abstractions:抽象包,一大堆的接口 Microsoft.Extensions.Configuration.Binder:提供一大堆的扩展,比如类型转换 Microsoft.Extensions.Configuration.Json:json实现 Microsoft.Extensions.Configuration.CommandLine:命令行实现 Microsoft.Extensions.Configuration.EnvironmentVariables:环境变量实现 Microsoft.Extensions.Primitives:ChangeToken ### 基本使用 ```C# //创建配置管理器 ConfigurationManager configuration = new ConfigurationManager(); configuration.SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("config.json")//添加json文件 .AddEnvironmentVariables();//添加系统环境变量 //打印环境变量 Console.WriteLine(configuration.GetSection("path").Value); //读取josn var student = configuration.GetSection("student"); Console.WriteLine(student["name"]); Console.WriteLine(configuration["student:name"]); //类型转换:需要安装Microsoft.Extensions.Configuration.Binder支持 Console.WriteLine(configuration.GetSection("student:age").Get()); ``` ### 自定义 IConfigurationProvider:表示我们的配置的数据源,可以是任意形式具体由自己实现,但是你必须处理成key-value的形式。 IConfigurationSource:用于配置和构建IConfigurationProvider的派生类型 ```C# //配置提供器选项:用于提供配置选项 public class IsonConfigurationSource : IConfigurationSource { public string? Url { get; set; } public bool ReloadOnChange { get; set; } public IsonConfigurationSource() { } public IsonConfigurationSource(string url) { Url = url; } public IConfigurationProvider Build(IConfigurationBuilder builder) { if (Url == null) { throw new ArgumentNullException(nameof(Url)); } return new IsonConfigurationProvider(this); } } //配置提供器:配置源逻辑 public class IsonConfigurationProvider : IConfigurationProvider { private ConcurrentDictionary values = new ConcurrentDictionary(); private IsonConfigurationSource options; private CancellationTokenSource? tokenSource; public IsonConfigurationProvider(IsonConfigurationSource options) { this.options = options; //如果需要监听 if (this.options.ReloadOnChange) { Watch(); } } private void Watch() { //注册事件 ChangeToken.OnChange(GetReloadToken, () => { Load(); }); //模拟更改 var t = new Thread(() => { while (true) { var token = tokenSource; tokenSource = null; //每3s之后发生更改 Thread.Sleep(3000); //触发事件,触发之前一定要将tokenSource设置成null token!.Cancel(); } }); t.Start(); } public IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath) { return values.Keys; } public IChangeToken GetReloadToken() { lock (this) { if (tokenSource == null) { tokenSource = new CancellationTokenSource(); } return new CancellationChangeToken(tokenSource!.Token); } } public void Load() { //假设我们从第三方地址获取 //var client = new HttpClient(); //var response = client.GetAsync(source.Url).GetAwaiter().GetResult(); //var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); values.TryAdd("t1", "1111111"); values.TryAdd("t2", "2222222"); Console.WriteLine("ison文件已加载..."); } public void Set(string key, string value) { values.TryAdd(key, value); } public bool TryGet(string key, out string value) { var flag = values.TryGetValue(key, out string? data); value = data ?? string.Empty; return flag; } } //扩展IConfigurationBuilder public static class IsonConfigurationExtensions { public static IConfigurationBuilder AddJsonUrl(this IConfigurationBuilder builder, string url) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (string.IsNullOrEmpty(url)) { throw new ArgumentException(nameof(url)); } return builder.Add(new IsonConfigurationSource(url)); } public static IConfigurationBuilder AddJsonUrl(this IConfigurationBuilder builder, Action configure) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } var source = new IsonConfigurationSource(); configure(source);//执行用户配置 return builder.Add(source); } ``` ### ChangeToken ChangeToken:将changeToken生产者和changeToken消费者进行绑定。 IChangeToken:传播已发生更改的通知(也可以自己实现)。可以注册回调在事件发生时执行。 CancellationChangeToken:IChangeToken的一种实现,通过取消令牌来进行通知。 CancellationTokenSource:通知CancellationToken,告知其应被取消,执行回调。 ```C# internal class Program { static void Main(string[] args) { var provider = new FileConfigurationProvider(); //绑定 provider.Watch(); new TaskCompletionSource().Task.Wait(); } } /// /// 文件配置程序超类 /// public class FileConfigurationProvider { private CancellationTokenSource? tokenSource; public void Load() { Console.WriteLine($"[{DateTime.Now}]文件已加载..."); } public void Watch() { //将changeToken生产者和changeToken消费者进行绑定(订阅) ChangeToken.OnChange(GetReloadToken, Load); //触发Change事件,通知更新 var t = new Thread(() => { while (true) { Thread.Sleep(3000); var t = tokenSource; tokenSource = null; //注意,取消之前一定要将tokenSource设置成null,不然会引发堆栈异常 //因为通知了取消,就会获取changeChange,但是你没有设置成null,OnChange认为又发生了取消通知 //会一直循环 t!.Cancel();//执行回调,发布取消事件。 } }); t.Start(); } /// /// 更新令牌,通过该令牌可以注册回调,用于执行更新通知。 /// /// public IChangeToken GetReloadToken() { lock (this) { //如果被消费就创建一个新的 if (tokenSource == null) { tokenSource = new CancellationTokenSource(); } return new CancellationChangeToken(tokenSource.Token); } } } ``` ## Logger ### package说明 Microsoft.Extensions.Logging.Abstractions:抽象包,一大堆的接口 Microsoft.Extensions.Logging:提供一些基本的实现 Microsoft.Extensions.Logging.Configuration:支持配置 Microsoft.Extensions.Logging.Console:打印到控制台的实现 ### 基本使用 ILoggingBuilder:日志是基于容器实现的。构建日志的过程就是往容器里添加服务。 #### 内置容器创建 ```C# //创建日志工厂,注意这里的设计模式,用到了Factory和Builder模式 ILoggerFactory factory = LoggerFactory.Create(configure => { //configure是ILoggingBuilder用于构建配置Factory //需要Microsoft.Extensions.Logging.Console的支持 configure.AddConsole(c => { c.FormatterName = ConsoleFormatterNames.Simple; }); //需要Microsoft.Extensions.Logging.Debug的支持 configure.AddDebug(); //configure.AddFile();//自定义扩展 configure.SetMinimumLevel(LogLevel.Information); }); //获取一个名为Program的日志实列 //注意:CreateLogger返回的是微软的一个实现,里面做了大量的封装,用于简化你的封装成本。 ILogger logger1 = factory.CreateLogger(); //有时候无法使用泛型的使用可以使用这个api ILogger logger2 = factory.CreateLogger(nameof(Program)); //推荐使用占位符,而不是字符串拼接,因为字符串拼接即使日志不打印,也要拼接即调用ToString,性能不行 logger1.LogInformation("Hello World!{0}", DateTime.Now);//只有打印的时候,才会计算Now并且调用ToString ``` #### 现有容器创建 ```C# public class LoggingBuilder : ILoggingBuilder { public IServiceCollection Services { get; } public LoggingBuilder(IServiceCollection services) { Services = services; } } //创建一个容器 var services = new ServiceCollection(); //注册日期工厂和ILogger泛型实现 services.AddLogging(); //创建日志Builder var loggingBuilder = new LoggingBuilder(services); //注意loggingBuilder会把服务注册到我们的现有容器里 loggingBuilder.AddConsole(); ``` ### 日志过滤器 ```C# ILoggerFactory factory = LoggerFactory.Create(configure => { configure.AddConsole(c => { c.FormatterName = ConsoleFormatterNames.Simple; }); //作用于所有日志提供程序 configure.AddFilter((categorName, level) => { if (categorName.StartsWith("Microsoft.EntityFramework")) { return true; } return false; }); //只作用与指定日志提供程序 //configure.AddFilter((categorName, level) => //{ // if (categorName.StartsWith("Microsoft.EntityFramework")) // { // return true; // } // return false; //}); }); var logger1 = factory.CreateLogger("Microsoft.EntityFramework"); var logger2 = factory.CreateLogger("Microsoft.AspNetCore"); //推荐使用占位符,而不是字符串拼接,因为字符串拼接即使日志不打印,也要拼接,调用toString性能不行 logger1.LogInformation("fff{0}", DateTime.Now); logger2.LogInformation("fff", DateTime.Now); ``` ### 日志配置 ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Information", "Microsoft.EntityFramework": "Warning" }, "Console": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Information", "Microsoft.EntityFramework": "Information" } }, "File": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Information", "Microsoft.EntityFramework": "Warning" } } } } ``` ```C# //创建配置 var configuration = new ConfigurationManager(); configuration.SetBasePath(Directory.GetCurrentDirectory()); configuration.AddJsonFile("config.json"); ILoggerFactory factory = LoggerFactory.Create(configure => { //使用配置文件 configure.AddConfiguration(configuration.GetSection("Logging")); configure.AddConsole(c => { c.FormatterName = ConsoleFormatterNames.Simple; }); configure.AddFile(); }); factory.CreateLogger("Microsoft.EntityFramework").LogInformation("ef core.."); factory.CreateLogger("Microsoft.AspNetCore").LogInformation("aspnet core.."); factory.CreateLogger("HuaJianDao.Logging").LogInformation("logging.."); ``` ### 自定义 ```C# //日志提供程序 //指定提供程序别名:配置时有用 [ProviderAlias("File")] public class FileLoggerProvider : ILoggerProvider { public FileLoggerProvider() { } private LoggerExternalScopeProvider ScopeProvider = new LoggerExternalScopeProvider(); public ILogger CreateLogger(string categoryName) { var logger = new FileLogger(categoryName, ScopeProvider); logger.ScopeProvider = new LoggerExternalScopeProvider(); return logger; } public void Dispose() { //用于释放资源 } } //日志记录器 internal class FileLogger : ILogger { private readonly string categoryName; public IExternalScopeProvider ScopeProvider { get; set; } public FileLogger(string categoryName, IExternalScopeProvider scopeProvider) { this.categoryName = categoryName; this.ScopeProvider= scopeProvider; } public IDisposable BeginScope(TState state) { return ScopeProvider.Push(state); } public bool IsEnabled(LogLevel logLevel) { return logLevel != LogLevel.None; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { var currentPath = Directory.GetCurrentDirectory(); var path = Path.Combine(currentPath, $"{DateTime.Now:yyyy-MM-dd}.log"); var message = formatter(state,exception); System.IO.File.AppendAllText(path, $"{logLevel}: {categoryName} {DateTime.Now}\r\n{message}\r\n"); } } //文件扩展 public static class FileLoggerExtensions { public static ILoggingBuilder AddFile(this ILoggingBuilder builder) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); return builder; } } ``` ### NLog https://nlog-project.org/ ```xml ``` ```C# public static void TestNLog() { //https://nlog-project.org/config ILoggerFactory factory = LoggerFactory.Create(configure => { //使用配置文件 configure.AddNLog();//面向微软的接口编程,可以享受第三方的扩展 configure.AddConsole(); }); var logger = factory.CreateLogger>(); logger.LogInformation("uuuuuuuuuu"); } ``` ## Options ### package说明 [ASP.NET Core 中的选项模式 | Microsoft Learn](https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0) Microsoft.Extensions.Options:选项的核心包,扩展IServiceCollection接口,只支持内存配置。 Microsoft.Extensions.Options.ConfigurationExtensions:配置文件的扩展,支持IConfiguration进行配置。 Microsoft.Extensions.DependencyInjection:选项必须配合容器使用 Microsoft.Extensions.Options.DataAnnotations:支持数据注解验证 IOptions:用于从容器解析选项。Singleton级别,只会加载一次配置。 IOptionsSnapshot:在每次请求时应重新计算选项的方案中有用,scope级别。支持命名配置 IOptionsMonitor:监听并通知更改,Singleton级别。支持命名配置 IOptionsFactory:管理Options实列,包括验证,配置的委托瞬时的。(工厂模式) IConfigureOptions:配置选项的委托 IPostConfigureOptions:配置选项的委托,在IConfigureOptions委托执行之后执行 IOptionsChangeTokenSource:用于监听IConfiguration的更改通知 IValidateOptions:配置选项之后的验证 ### 源码解读 注意:选项必须提供无参构造器,具体阅读OptionsFactory的源码底层采用的是反射。选项是.NET中的一个非常重要的概念,通过这节课程大家需要掌握如何灵活更改框架的选项。对于一些需要编写框架的同学也有很大的帮助。但凡以Options结尾的一般都是选项模式。 ```C# public static IServiceCollection Configure(this IServiceCollection services, string name, Action configureOptions) where TOptions : class { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (configureOptions == null) { throw new ArgumentNullException(nameof(configureOptions)); } services.AddOptions(); services.AddSingleton>(new ConfigureNamedOptions(name, configureOptions)); return services; } ``` ```C# //注册Options核心组件 public static IServiceCollection AddOptions(this IServiceCollection services) { if (services == null) { throw new ArgumentNullException(nameof(services)); } services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>))); services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>))); services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>))); services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>))); services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>))); return services; } ``` 注册过程简述: 1.调用Configure方法会将配置选项的委托IConfigureOptions(不是选项本身)添加到容器,同一个选项可以调用多个Configure,注册多个IConfigureOptions实列。 2.调用PostConfigure方法会将配置选项的委托IPostConfigureOptions(不是选项本身)添加到容器,同一个选项可以调用多个PostConfigure,注册多个IPostConfigureOptions实列。和Configure类型都是用于保存配置选项的委托。 3.IConfigureNamedOptions接口继承了IConfigureOptions接口,而ConfigureNamedOptions实现了IConfigureNamedOptions接口。用于保存配置选项的委托和名字。 3.调用Configure方法,会调用AddOptions,AddOptions注册了IOptions、IOptionsSnapshot、IOptionsMonitor、IOptionsFactory、IOptionsMonitorCache到容器里。 4.IOptionsFactory,负责管理,创建,配置、验证选项。源码可以知道IOptionsFactory依赖注入了所有的(同一个选项)的IConfigureOptions和IPostConfigureOptions的委托,先按顺序(注册的顺序)执行所有的Configure委托,然后在按顺序执行所有的PostConfigure委托。因此PostConfigure一定在所有的Configure之后执行。然后在执行所有的验证器。IOptionsFactory是通过反射创建选项的,因此选项不支持依赖注入,必须提供无参数构造器。 5.IOptions接口的实现类是单实列的。通过依赖IOptionsFactory完成选项的管理。不支持命名选项,不支持配置更改计算(单实例)。 6.IOptionsSnapshot是Scope级别的但是有缓存策略,通过依赖IOptionsFactory完成选项的管理。缓存由于是实列的,而实列的生命周期是scope级别,因此缓存也是scope级别。不要通过根容器解析scope级别的实列,因为不会根容器释放。支持命名选项,支持配置的更改计算,每次都会从配置文件读取并绑定到选项。 7.IOptionsMonitor是单实例的,通过依赖IOptionsFactory完成选项的管理。支持命名选项,支持配置更改计算,有缓存机制,可以注册选项更改通知回调。 ```C# public class MvcOptions { public string Uri { get; } } static void TestSort() { var services = new ServiceCollection(); //name为Empty services.Configure(a => { Console.WriteLine(11); a.Url = "132"; }); services.PostConfigure(a => { Console.WriteLine(33); a.Url = "132"; }); services.Configure(a => { Console.WriteLine(22); a.Url = "132"; }); services.PostConfigure(a => { Console.WriteLine(44); a.Url = "132"; }); var sp = services.BuildServiceProvider(); var optionsFactory = sp.GetRequiredService>(); var options = optionsFactory.Create(Options.DefaultName); } ``` ### 核心方法 注意:Configure和AddOptions都不是直接把选择注入到容器,而是把选项的配置委托注入到容器。因此你可以执行无数多个Configre和AddOptions。 ```C# //1.通过委托来配置选项,同一个选项可以按顺序执行多个Configure。 services.Configure(a => { a.Url = "132"; }); //2.在所有的Configure执行之后配置选项。同一个选项可以按顺序执行多个PostConfigure。 services.PostConfigure(a => { a.Url = "132"; }); //AddOptions返回一个OptionsBuilder,可以连续配置选项,本质还是执行Configure和PostConfigure services.AddOptions() .Bind(configuration.GetSection("MvcOptions")) .Configure(a=>a.Url="123"); ``` ### 解析选项 ```c# var services = new ServiceCollection(); services.Configure(a => { a.Url = "132"; }); var sp = services.BuildServiceProvider(); var optionsFactory = sp.GetRequiredService>(); var options = sp.GetRequiredService>(); var optionsMonitor = sp.GetRequiredService>(); var optionsSnapshot = sp.GetRequiredService>(); ``` ### 注入选项 吧Options直接注入到容器,可以通过IOptions、IOptionsSnapshot、IOptionsMonitor、IOptionsFactory ```C# static void TestDiOptions() { var services = new ServiceCollection(); services.Configure(a => { Console.WriteLine(22); a.Url = "132"; }); services.AddSingleton(sp => { return sp.GetRequiredService>().Value; }); //必须使用Scoped,因为IOptionsSnapshot是Scoped的,如果是单实列的话,就丢失了自动更改的性质了 //services.AddScoped(sp => //{ // return sp.GetRequiredService>().Value; //}); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService(); } ``` ### 监听更改 ``` { "MvcOptions": { "Url": "123" }, "WebOptions": { "Url": 456 } } ``` ```C# public class MvcOptions { public string? Url { get; set; } } static void OnChange() { var services = new ServiceCollection(); var configuration = GetConfiguration(); services.Configure(configuration.GetSection("MvcOptions")); var container = services.BuildServiceProvider(); while (true) { Thread.Sleep(1000); using (var scope = container.CreateScope()) { var o1 = scope.ServiceProvider.GetRequiredService>(); var o2 = scope.ServiceProvider.GetRequiredService>(); var o3 = scope.ServiceProvider.GetRequiredService>(); Console.WriteLine("=================="); Console.WriteLine($"IOptions:{o1.Value.Url}"); Console.WriteLine($"IOptionsSnapshot:{o2.Value.Url}"); Console.WriteLine($"IOptionsMonitor:{o3.CurrentValue.Url}"); o3.OnChange(o => { Console.WriteLine("选项发生更改了"); }); } } } static IConfiguration GetConfiguration() { var configuration = new ConfigurationManager(); configuration.SetBasePath(Directory.GetCurrentDirectory()); configuration.AddJsonFile("config.json", false, true); return configuration; } ``` ### 命名选项 ```json { "MvcOptions": { "Url": "123" }, "WebOptions": { "Url": 456 } } ``` ```C# public class MvcOptions { public string? Url { get; set; } } static void TestNamed() { var services = new ServiceCollection(); var configuration = GetConfiguration(); //name=Options.DefaultName services.Configure(configuration.GetSection("MvcOptions")); //name="o1" services.Configure("o1", configuration.GetSection("MvcOptions")); //name="o2" services.Configure("o2", configuration.GetSection("WebOptions")); var container = services.BuildServiceProvider(); var o1 = container.GetRequiredService>(); var o2 = container.GetRequiredService>(); //name="o1" Console.WriteLine("IOptionsSnapshot:Named:" + o1.Get("o1").Url); //name=Options.DefaultName Console.WriteLine("IOptionsSnapshot:Value:" + o1.Value.Url); //name="o2" Console.WriteLine("IOptionsMonitor:Named:" + o2.Get("o2").Url); //name=Options.DefaultName Console.WriteLine("IOptionsMonitor:Value:" + o2.CurrentValue.Url); var optionsFactory = sp.GetRequiredService>(); var options = optionsFactory.Create(Options.DefaultName); } ``` 只有IOptionsFactory、IOptionsSnapshot、IOptionsMonitor支持命名配置 ### 接口配置 ```C# //通过该源码发现,我们可以通过实现IPostConfigureOptions接口,并注入到容器来进行配置选项 public static IServiceCollection PostConfigure(this IServiceCollection services, string name, Action configureOptions) where TOptions : class { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (configureOptions == null) { throw new ArgumentNullException(nameof(configureOptions)); } services.AddOptions(); services.AddSingleton>(new PostConfigureOptions(name, configureOptions)); return services; } ``` ```C# public class MvcOptions { public string? Url { get; set; } } //你也可以实现IConfigureOptions但是IConfigureOptions的执行顺序优先级比较低(要学会举一反三) internal class MvcOptionsPostConfigureOptions : IPostConfigureOptions { public void PostConfigure(string name, MvcOptions options) { options.Url = "789"; //可以编写验证逻辑 //if (options.Url == "123") //{ // throw new InvalidDataException("Url不能等于123"); //} } } public static void TestIPostConfigureOptions() { var services = new ServiceCollection(); services.Configure(s => s.Url = "111"); services.AddSingleton, MvcOptionsPostConfigureOptions>(); var container = services.BuildServiceProvider(); var o2 = container.GetRequiredService>(); } ``` ### 服务配置 AddOptions会返回可以OptionsBuilder,OptionsBuilder中支持DI的方式来配置选项,最多支持5个。 ```C# internal class MvcOptionsDep { public void Configure(MvcOptions options) { options.Url = "6666"; } } public static void TestDep() { var services = new ServiceCollection(); //注册di服务 services.AddSingleton(); services.AddOptions()//注册选项 .Configure(a=>a.Url="123")//第一个配置 .PostConfigure((options, dep) => //后续配置 { dep.Configure(options);//调用MvcOptionsDep中的方法来进行配置 }); var container = services.BuildServiceProvider(); var o2 = container.GetRequiredService>(); } ``` ### 验证选项 注意验证是一定是在Configure和PostConfigure之后执行的 1.委托验证 ```C# public static void TestValidateDelegate() { try { var services = new ServiceCollection(); //不是把选项注入到容器,只是注入了该选项的委托 services.Configure(a => a.Url = "123"); //得到OptionsBuilder,进行验证,本质还是执行Configure(可以执行无数多个Configre) services.AddOptions() .Validate(options => { if (options.Url == null) { return false; } if (!options.Url.StartsWith("aa")) { return false; } return true; }, "必须以aa开头"); var container = services.BuildServiceProvider(); var options = container.GetRequiredService>(); Console.WriteLine(options.Value.Url); } catch (Exception ex) { Console.WriteLine(ex.Message); } } ``` 2.注解验证 需要安装Microsoft.Extensions.Options.DataAnnotations包 ```C# public class MvcOptions { [RegularExpression(@"^aa",ErrorMessage ="必须以aa开头")] public string? Url { get; set; } } public static void TestValidateDataAnnotations() { try { var services = new ServiceCollection(); //不是把选项注入到容器,只是注入了该选项的委托 services.Configure(a => a.Url = "123"); //得到OptionsBuilder,进行验证,本质还是执行Configure(可以执行无数多个Configre) services.AddOptions() .ValidateDataAnnotations(); var container = services.BuildServiceProvider(); var options = container.GetRequiredService>(); Console.WriteLine(options.Value.Url); } catch (Exception ex) { Console.WriteLine(ex.Message); } } ``` ## AOP 面向切面编程AOP的对面向对象编程OOP的一个补充,它的特点是将系统逻辑和业务逻辑采取《非侵入式》分离。我们把系统封装成一个一个的切面(单一职责)进行顺意编排组合,插入(织入)到业务逻辑的执行过程(织入点)。 系统逻辑:异常处理,身份认证,授权,mvc,数据校验,事务处理。 业务逻辑:就是我们的业务Service。 切面:用于封装系统逻辑,比如身份认证filter,或者中间件 切入点:就是管道的位置。名词 织入:就是插入到管道的切入点的过程。动词 AOP的特点: 1.非侵入式 2.低耦合 3.代码服用 4.单一职责 5.可插拔 实现方式: 1.管道链,比如aspnetcore的中间件,mvc中的Filter 2.静态代理:思考如何加强一个List,使得在插入时打印日志? 3.动态代理:Emit 4.三种模式都需要通过一些技术进行串联,实现链式调用,构成管道。静态代理通过接口进行串联,动态代理通过反射进行串联。管道通过接口获取委托进行串联。委托本质也是接口。 代理:就是增强,代理对象必须尽量实现目标对象的功能,在此基础上进行加强。比如vpn,你的电脑的网络就是目标对象,vpn就是代理服务,代理服务起码得实现联网功能吧,然后对网络进行加强,访问到一些你的本机网络访问不到的东西。 掌握了AOP技术我们可以实现很多好处,做到非侵入式的增强业务逻辑。 ```C# //侵入式方案,把这个看懂。后面就是围绕这个开展,高出非侵入式 public static void A() { Console.WriteLine("A:开始"); B();//A,B的调用关系强行绑定,有侵入性 Console.WriteLine("A:结束"); } public static void B() { Console.WriteLine("B:开始"); C(); Console.WriteLine("B:结束"); } public static void C() { Console.WriteLine("Hello World"); } public static void Dobasic() { A(); } ``` ### 静态代理 1.代理就是实现目标对象的标准(接口),在目标方法执行之前和之后进行逻辑织入的过程。代理的目的就是为了加强。代理不负责实现接口,一般通过target来实现接口。即代理除了可以增强之外还能简化接口的实现。 2.静态代理就是在代理之前就已经确定了代理关系。需要自己实现标准并编写代理类。代理类中的逻辑只能代理一些标准(实现多个接口)。无法代理所有标准。 3.静态代理可以实现不改变目标对象的源码的情况下进行加强,完成目标对象的能力,并且在此基础之上进行加强。 4.可以简化实现的成本,不改变业务代码,只需要编写额外的增强逻辑。不需要关系具体的业务实现。 5.代理和代理直接通过接口可以进行互相代理,链式调用,顺意编排组合,实现系统的多样化。 ```C# /// /// 定义标准1 /// public interface IPhoneService { string Mobile { get; set; } string Message { get; set; } void Send(); } //实现标准-不是代理模式 public class PhoneService : IPhoneService { public string Mobile { get; set; } public string Message { get; set; } public PhoneService(string mobile, string message) { Mobile = mobile; Message = message; } public virtual void Send() { Console.WriteLine($"已发送短信:{Message}到{Mobile}"); } } //代理模式: //1.实现目标对象的标准 //2.依赖目标对象(被代理对象) //3.业务织入 public class PhoneServiceProxy : IPhoneService//实现标准 { private readonly IPhoneService _target; public PhoneServiceProxy1(IPhoneService target) { _target = target; } public string Mobile { get => _target.Mobile; set => _target.Mobile = value; } public string Message { get => _target.Message; set => _target.Message = value; } /// /// 子类重写父类方法 /// public void Send() { Console.WriteLine("Proxy1:已对手机号进行验证"); _target.Send(); Console.WriteLine("Proxy1:已确认对方已经收到"); } } ``` ```C# /// /// 定义标准1 /// public interface IPhoneService { string Mobile { get; set; } string Message { get; set; } void Send(); } /// /// 定义标准2 /// public interface IEmailService { string Email { get; set; } string Message { get; set; } void Send(); } /// /// 业务逻辑1 /// public class PhoneService : IPhoneService { public string Mobile { get; set; } public string Message { get; set; } public PhoneService(string mobile, string message) { Mobile = mobile; Message = message; } public virtual void Send() { Console.WriteLine($"已发送短信:{Message}到{Mobile}"); } } /// /// 业务逻辑2 /// public class EmailService : IEmailService { public string Email { get; set; } public string Message { get; set; } public EmailService(string email, string message) { Email = email; Message = message; } public virtual void Send() { Console.WriteLine($"已发送邮件:{Message}到{Email}"); } } /// /// 切面1:校验能力(系统逻辑) /// taget方式 /// public class PhoneServiceProxy1 : IPhoneService//实现标准1 { private readonly IPhoneService _target; public PhoneServiceProxy1(IPhoneService target) { _target = target; } /// /// 子类重写父类方法 /// public void Send() { Console.WriteLine("Proxy1:已对手机号进行验证"); _target.Send(); Console.WriteLine("Proxy1:已确认对方已经收到"); } } /// /// 切面2:加速能力(系统逻辑) /// public class PhoneServiceProxy2 : IPhoneService//实现标准1 { private readonly IPhoneService _target; public PhoneServiceProxy2(IPhoneService target) { _target = target; } /// /// 子类重写父类方法 /// public void Send() { Console.WriteLine("Proxy2:已开启加速通道"); _target.Send(); Console.WriteLine("Proxy2:已关闭加速通道"); } } //test public static void TestStaticProxy() { //目标对象 IPhoneService target = new PhoneService("10088", "你好啊!"); //切面1:验证,对target进行代理 IPhoneService proxy1 = new PhoneServiceProxy1(target); //切面2:加速,对proxy1进行代理 IPhoneService proxy2 = new PhoneServiceProxy2(proxy1); //执行 proxy2.Send(); //思考如果要实现IEmailService标准,是不是要重写实现类了? } ``` ### 动态代理 #### Castle.Core 动态代理和静态代理的区别就是,代理类由工具生成,需要在运行时确认代理类已经代理关系。代理类中的逻辑写到拦截器里面,可以进行复用。缺点是性能差。里面涉及到大量反射技术。 Castle.Core:原理就是通过子类继承父类或者实现父类标准,通过Castle.Core自动帮你生成代理类,通过一个叫拦截器的东西编写代理类要执行的业务逻辑。Castle.Core会帮你生成代理类,并将拦截器织入到代理类中。 动态代理通过invocation进行串联,本质是反射。 ```C# /// /// 定义标准1 /// public interface IPhoneService { string Mobile { get; set; } string Message { get; set; } void Send(); } /// /// 定义标准2 /// public interface IEmailService { string Email { get; set; } string Message { get; set; } void Send(); } /// /// 业务逻辑1 /// public class PhoneService : IPhoneService { public string Mobile { get; set; } public string Message { get; set; } public PhoneService(string mobile, string message) { Mobile = mobile; Message = message; } public virtual void Send() { Console.WriteLine($"已发送短信:{Message}到{Mobile}"); } } /// /// 业务逻辑2 /// public class EmailService : IEmailService { public string Email { get; set; } public string Message { get; set; } public EmailService(string email, string message) { Email = email; Message = message; } public virtual void Send() { Console.WriteLine($"已发送邮件:{Message}到{Email}"); } } /// /// 代理1:任意标准 /// public class ShareInterceptor1 : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("Proxy1:已对接收方进行验证"); invocation.Proceed();//执行下一个拦截器或者目标方法 Console.WriteLine("Proxy1:已确认对方已经收到"); } } /// /// 代理2:任意标准 /// public class ShareInterceptor2 : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("Proxy2:已开启加速通道"); invocation.Proceed();//执行下一个拦截器或者目标方法 Console.WriteLine("Proxy2:已关闭加速通道"); } } //通过Castel生成代理类 public static void TestDynamicProxy1() { //创建代理生成器 var generator = new ProxyGenerator(); var target1 = new PhoneService("10088", "你好啊!"); var target2 = new EmailService("1123@116.com", "你好啊!"); var interceptor1 = new ShareInterceptor1();//代理1,拦截器1,不需要去实现指定的标准 var interceptor2 = new ShareInterceptor2();//代理2,拦截器2,不需要去实现指定的标准 //使用代理1和代理2去代理手机的标准 IPhoneService dynamicProxy1 = generator.CreateInterfaceProxyWithTarget(target1, interceptor1, interceptor2); dynamicProxy1.Send(); //代理邮件的标准 IEmailService dynamicProxy2 = generator.CreateInterfaceProxyWithTarget(target2, interceptor1, interceptor2); dynamicProxy2.Send(); } ``` #### 手写Castle.Core的代理类 思考: generator创建的是什么类型的实列?显然不可能是已有的类型。因为它把拦截器织入进去了。而且没有修改我们的代码,站在面向对象的角度来看只能是实现了我们的接口,Emit动态实现了下面的代码 多个拦截器和目标对象(被代理者)通过Invocation进行串联。Invocation中的Arguments完成链式调用。 手动通过Invocation进行串联 ```c# //假设有三个拦截器 //第一个拦截器invocation1:Proxy=interceptor2,Method=Intercept,argument=invocation2 //第二个拦截器invocation2:Proxy=interceptor3,Method=Intercept,argument=invocation3 //第三个拦截器invocation2:Proxy=target,Method=method,argument=arguments //手动实现 public IInvocation GetInvocation(Stack stack, object target, Method method, objuect arguments) { var invocation1 = new Invocation() { Proxy = interceptor2, Method = typeof(IInterceptor).GetMethod(nameof(IInterceptor.Intercept)), Arguments = new object[] { new Invocation() { Proxy = interceptor3, Method = typeof(IInterceptor).GetMethod(nameof(IInterceptor.Intercept)), Arguments = new object[] { new Invocation() { Proxy = target, Method = method, Arguments = arguments } } } } } } //递归实现 public IInvocation GetInvocation(Stack stack, object target, Method method, objuect arguments) { if(stack.Any()) { var proxy = stack.Pop(); return new Invocation() { Proxy = proxy, Method = typeof(IInterceptor).GetMethod(nameof(IInterceptor.Intercept)), Agrumtns = GetInvocation(stack,method,argumtns) }; } else { return new Invocation() { Proxy = target, Method = method, Agrumtns = arguments }; } } ``` ```C# //Castel.Core自动帮我们生成了下面这个类 public class CastelPhoneServiceProxy : IPhoneService { private IPhoneService _taget; private IInterceptor[] _interceptors; public CastelPhoneServiceProxy(IPhoneService taget, IInterceptor[] interceptors) { _taget = taget; _interceptors = interceptors; } public string Mobile { get => _taget.Mobile; set => _taget.Mobile = value; } public string Message { get => _taget.Message; set => _taget.Message = value; } public void Send() { var stack = new Stack(_interceptors.Reverse()); if (stack.Any()) { var item = stack.Pop(); var invocation = GetNextInvocation(stack); item.Intercept(invocation); } else { _taget.Send(); } } /// /// 递归获取Invocaltion /// /// /// private IInvocation GetNextInvocation(Stack stack) { if (stack.Any()) { var next = stack.Pop(); return new Invocaltion { Arguments = new object[] { //递归 GetNextInvocation(stack) }, Proxy = next, Method = typeof(IInterceptor).GetMethod(nameof(IInterceptor.Intercept)) ?? throw new NullReferenceException() }; } else { return new Invocaltion { Arguments = new object[] { }, Proxy = _taget, Method = _taget.GetType().GetMethod(nameof(IPhoneService.Send)) ?? throw new NullReferenceException() }; } } } //实现一些castle.core的接口 public class Invocaltion : IInvocation { public object[] Arguments { get; set; } public Type[] GenericArguments { get; set; } public object InvocationTarget { get; set; } public MethodInfo Method { get; set; } public MethodInfo MethodInvocationTarget { get; set; } public object Proxy { get; set; } public object ReturnValue { get; set; } public Type TargetType { get; set; } public IInvocationProceedInfo CaptureProceedInfo() { throw new NotImplementedException(); } public object GetArgumentValue(int index) { throw new NotImplementedException(); } public MethodInfo GetConcreteMethod() { throw new NotImplementedException(); } public MethodInfo GetConcreteMethodInvocationTarget() { throw new NotImplementedException(); } public void Proceed() { Method.Invoke(Proxy, Arguments); } public void SetArgumentValue(int index, object value) { throw new NotImplementedException(); } } ``` #### EMIT实现 ```C# //链路器 public class EmitInvocation { private object? proxy; private MethodInfo? method; private object[]? arguments; public EmitInvocation(object? proxy, MethodInfo? method, object[]? arguments) { this.proxy = proxy; this.method = method; this.arguments = arguments; } public void Proceed() { method?.Invoke(proxy, arguments); } } //拦截器 public interface IEmitInteceptor { void Intercept(EmitInvocation invocation); } //实现拦截器1 public class EmitInteceptor1 : IEmitInteceptor { public void Intercept(EmitInvocation invocation) { Console.WriteLine("prox1:start"); invocation.Proceed(); Console.WriteLine("prox1:end"); } } //实现拦截器1 public class EmitInteceptor2 : IEmitInteceptor { public void Intercept(EmitInvocation invocation) { Console.WriteLine("prox2:start"); invocation.Proceed(); Console.WriteLine("prox2:end"); } } //该工具类帮助我们少写emit代码 public static class EmitProxyInvoker { public static EmitInvocation GetNextInvocation(Stack stack, MethodInfo method, object target, object[] arguments) { if (stack.Any()) { var next = stack.Pop(); arguments = new object[] { //递归 GetNextInvocation(stack, method, target, arguments) }; return new EmitInvocation(next, typeof(IEmitInteceptor).GetMethod(nameof(IEmitInteceptor.Intercept)), arguments); } else { return new EmitInvocation(target, method, arguments); } } public static void Invoke(IEmitInteceptor[] interceptors, MethodInfo method, object target, object[] arguments) { var stack = new Stack(interceptors.Reverse()); if (stack.Any()) { var item = stack.Pop(); var invocation = GetNextInvocation(stack, method, target, arguments); item.Intercept(invocation); } else { method.Invoke(target, arguments); } } } //业务接口 public interface IEmitService { void Send(); } //将来要生成的代理类 public class EmitServiceProxy : IEmitService { private object _target; private IEmitInteceptor[] _inteceptors; public EmitService() { } public void Send() { var method = _target.GetType().GetMethod(nameof(EmitService.Send)); var arguments = new object[] { }; EmitProxyInvoker.Invoke(_inteceptors, method, _target, new object[] { }); } } public static class EmitProxyGenerator { static AssemblyBuilder _assemblyBuilder; static ModuleBuilder _moduleBuilder; static EmitProxyGenerator() { //创建一个程序集 var assemblyName = new AssemblyName("DynamicProxies"); _assemblyBuilder = AssemblyBuilder .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); //创建一个模块 _moduleBuilder = _assemblyBuilder.DefineDynamicModule("Proxies"); } public static TInterface Create(object target, params IEmitInteceptor[] inteceptor) where TInterface : class { #region 定义类型 //定义一个class,如果这个类型已定义直接返回,缓存 var typeName = $"{target.GetType().Name}EmitProxy"; var typeBuilder = _moduleBuilder.DefineType( typeName, TypeAttributes.Public,typeof(object), new Type[] { typeof(TInterface) }); #endregion #region 定义字段 //定义字段 var targetFieldBuilder = typeBuilder.DefineField("target", typeof(object), FieldAttributes.Private); var inteceptorFieldBuilder = typeBuilder.DefineField("inteceptor", typeof(IEmitInteceptor[]), FieldAttributes.Private); #endregion #region 定义构造器 //定义构造器 var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.ExplicitThis, new Type[] { typeof(object), typeof(IEmitInteceptor[]) }); //获取IL编辑器 var generator = constructorBuilder.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0);//加载this generator.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes) ?? throw new InvalidOperationException()); generator.Emit(OpCodes.Nop); generator.Emit(OpCodes.Nop); // this.age = age; generator.Emit(OpCodes.Ldarg_0);//加载this generator.Emit(OpCodes.Ldarg_1);//加载target参数 generator.Emit(OpCodes.Stfld, targetFieldBuilder);//加载target字段 // this.name = name; generator.Emit(OpCodes.Ldarg_0);//加载this generator.Emit(OpCodes.Ldarg_2);//加载inteceptor参数 generator.Emit(OpCodes.Stfld, inteceptorFieldBuilder);//加载inteceptor字段 generator.Emit(OpCodes.Ret); #endregion #region 实现接口 var methods = typeof(TInterface).GetMethods(); foreach (var item in methods) { var parameterTypes = item.GetParameters().Select(s => s.ParameterType).ToArray(); var methodBuilder = typeBuilder.DefineMethod(item.Name, MethodAttributes.Public| MethodAttributes.Final |MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, CallingConventions.Standard|CallingConventions.HasThis, item.ReturnType, parameterTypes); var generator1 = methodBuilder.GetILGenerator(); //init var methodInfoLocal = generator1.DeclareLocal(typeof(MethodInfo)); var argumentLocal = generator1.DeclareLocal(typeof(object[])); generator1.Emit(OpCodes.Nop); generator1.Emit(OpCodes.Ldarg_0); generator1.Emit(OpCodes.Ldfld, targetFieldBuilder); generator1.Emit(OpCodes.Callvirt, typeof(Type).GetMethod(nameof(Type.GetType),Type.EmptyTypes)); generator1.Emit(OpCodes.Ldstr, item.Name); generator1.Emit(OpCodes.Callvirt, typeof(Type).GetMethod(nameof(Type.GetMethod), new Type[] { typeof(string) })); generator1.Emit(OpCodes.Stloc, methodInfoLocal); generator1.Emit(OpCodes.Ldc_I4_0); generator1.Emit(OpCodes.Newarr, typeof(object)); generator1.Emit(OpCodes.Stloc, argumentLocal); generator1.Emit(OpCodes.Ldarg_0); generator1.Emit(OpCodes.Ldfld, inteceptorFieldBuilder); generator1.Emit(OpCodes.Ldloc_0); generator1.Emit(OpCodes.Ldarg_0); generator1.Emit(OpCodes.Ldfld, targetFieldBuilder); generator1.Emit(OpCodes.Ldc_I4_0); generator1.Emit(OpCodes.Newarr, typeof(object)); generator1.Emit(OpCodes.Call, typeof(EmitProxyInvoker).GetMethod(nameof(EmitProxyUtil.Invoke))); generator1.Emit(OpCodes.Nop); generator1.Emit(OpCodes.Ret); } #endregion //创建:这个type可以用一个线程安全的字典缓存起来,第二次需要这个代理类的时候,就不需要在生成一次emit代码了。 var type = typeBuilder.CreateType() ?? throw new ArgumentException(); var instance = Activator.CreateInstance(type, target, inteceptor); return (TInterface)instance; } } ``` #### 容器支持 ```C# public class DbContext { } public class AService { public DbContext DbContext { get; } public AService(DbContext context) { DbContext = context; } } public static void Test() { var services = new ServiceCollection(); services.AddScoped(); var generator = new ProxyGenerator(); //泛型-不支持动态注入 services.AddScoped(sp => { //通过容器解析依赖 var target = ActivatorUtilities.CreateInstance(sp); return generator.CreateClassProxyWithTarget(target); }); //反射-可以扫描批量注入 services.AddScoped(typeof(AService), sp => { //通过容器解析依赖 var target = ActivatorUtilities.CreateInstance(sp, typeof(AService)); return generator.CreateClassProxyWithTarget(target); }); } ``` ### 管道方式 #### 委托方式 1.通过委托构建管道 ```C# public delegate Task RequestDelegate(HttpContext context); public class HttpContext { } public class ApplicationBuilder { private readonly List> _componen public void Use(Func middleware) { _components.Add(middleware); } public void Use(Func, Task> middleware) { _components.Add((next) => { return async c => { await middleware(c, () => next(c)); }; }); } public void Use(Func middleware) { _components.Add((next) => { return async c => { await middleware(c, next); }; }); } public void Run(RequestDelegate handler) { _components.Add((next) => { return async c => { await handler(c); }; }); } //构建管道 public RequestDelegate Build() { RequestDelegate app = c => { throw new InvalidOperationException("无效的管道"); }; for (int i = _components.Count - 1; i > -1; i--) { app = _components[i](app); } return app; } } ``` #### 接口方式 2.通过接口构建管道 有点类型动态代理,动态代理是通过Invocation进行反射,而下面的方式是通过接口的方式。反射更加灵活,性能不行。 ```C# public interface IChain { Task NextAsync(); } public class FilterChain : IChain { private readonly IFilter _filter; private readonly HttpContext _context; private readonly IChain _next; public FilterChain(IFilter filter, HttpContext context, IChain next) { _filter = filter; _context = context; _next = next; } public async Task NextAsync() { await _filter.InvokeAsync(_context, _next); } } public class ServletChain : IChain { private readonly IServlet _servlet; private readonly HttpContext _context; public ServletChain(IServlet servlet, HttpContext context) { _servlet = servlet; _context = context; } public async Task NextAsync() { await _servlet.DoPostAsync(_context); } } public interface IFilter { Task InvokeAsync(HttpContext context, IChain chain); } public class Filter1 : IFilter { public async Task InvokeAsync(HttpContext context, IChain chain) { Console.WriteLine("身份认证开始"); await chain.NextAsync(); Console.WriteLine("身份认证结束"); } } public class Filter2 : IFilter { public async Task InvokeAsync(HttpContext context, IChain chain) { Console.WriteLine("授权认证开始"); await chain.NextAsync(); Console.WriteLine("授权认证结束"); } } public interface IServlet { Task DoPostAsync(HttpContext context); } public class HelloServlet : IServlet { public Task DoPostAsync(HttpContext context) { Console.WriteLine("Hello World"); return Task.CompletedTask; } } public class WebHost { private readonly List _filters = new List(); public void AddFilter(IFilter filter) { _filters.Add(filter); } public void Exeucte(HttpContext context, IServlet servlet) { //自行处理filter为空的情况,就是直接执行serlvet就好了 var stack = new Stack(_filters); var filter = stack.Pop(); var chain = GetFilterChain(context, servlet,stack); filter.InvokeAsync(context, chain); } private IChain GetFilterChain(HttpContext context, IServlet servlet, Stack filters) { if (filters.Any()) { var filter = filters.Pop(); var chain = GetFilterChain(context, servlet, filters); return new FilterChain(filter, context, chain); } else { return new ServletChain(servlet, context); } } } ``` ### AOP总结 1.代理分为静态代理和动态代理,静态代理需要自己编写代理类,动态代理由框架生成代理类。 2.代理和管道都需要通过接口(委托)进行链接,串联,形成链式调用。 3.动态代理慎用,因为涉及到反射技术,而且对异步支持不友好。 4.静态代理常用于加强已有类型,比如接口要求一个IList,我们已经拥有了一个list实列,我们需要在在list.Add方法时打印日志,此时我们可以不改变原有的list,通过静态代理实现IList接口来进行对原有的list加强。这个方法在更改框架的时候很有用。我们可以对原有的HttpContext,进行加强。 ## WebHost #### HttpContext ```C# public class HttpContext { public HttpRequest Request { get; }//请求 public HttpResponse Response { get; }//相应 public IFeatureCollection Features { get; }//用于记录请求过程中的实列 public IServiceProvider RequestServices { get; }//子容器 public HttpContext(HttpRequest request, IServiceProvider requestServices) { Request = request; Response = new HttpResponse(); RequestServices = requestServices; var features = requestServices.GetRequiredService(); Features = new FeatureCollection(features); } } public class HttpRequest { public string Url { get; set; } public string Method { get; set; } public string From { get; set; } } public class HttpResponse { //用于缓存数据包 public MemoryStream _stream = new MemoryStream(); public void Write(byte[] bytes) { _stream.Write(bytes, 0, bytes.Length); } public async Task WriteAsync(byte[] bytes) { await _stream.WriteAsync(bytes, 0, bytes.Length); } public void Write(string message) { Write(Encoding.UTF8.GetBytes(message)); } public async Task WriteAsync(string message) { await WriteAsync(Encoding.UTF8.GetBytes(message)); } public byte[] GetBuffer() { return _stream.GetBuffer(); } } ``` #### IApplicationBuilder ```C# //处理请求的委托 public delegate Task RequestDelegate(HttpContext context); //用于构建管道 public interface IApplicationBuilder { void Use(Func middleware); RequestDelegate Build(); } public class ApplicationBuilder : IApplicationBuilder { private List> _middlewares = new(); public void Use(Func middleware) { _middlewares.Add(middleware); } public RequestDelegate Build() { RequestDelegate app = async context => { await context.Response.WriteAsync("404"); }; for (int i = _middlewares.Count - 1; i > -1; i--) { app = _middlewares[i](app); } return app; } } //扩展Builder public static class IApplicationBuilderExtensions { //使得next不需要参数 public static void Use(this IApplicationBuilder app, Func, Task> middleware) { app.Use(next => { return async context => { await middleware(context, async () => await next(context)); }; }); } //使用委托来执行中间件的代码 public static void Use(this IApplicationBuilder app) where T : IMiddleware { app.Use(next => { return async context => { var middleware = ActivatorUtilities.CreateInstance(context.RequestServices, next); await middleware.InvokeAsync(context); }; }); } //断点中间件 public static void Run(this IApplicationBuilder app, RequestDelegate endpoint) { app.Use(_ => { return async context => { await endpoint(context); }; }); } } ``` #### IFeatureCollection ```C# /// /// 对象工厂,用于管理请求过程中的实列 /// public interface IFeatureCollection : IEnumerable> { TFeature? Get() where TFeature : class; void Set(TFeature? instance); } public class FeatureCollection : IFeatureCollection, IEnumerable> { private Dictionary target = new Dictionary(); public FeatureCollection() { } public FeatureCollection(IFeatureCollection list) { foreach (var item in list) { Set(item); } } public TFeature? Get() where TFeature : class { var key = typeof(TFeature); if (target.TryGetValue(key, out object? value)) { return value as TFeature; } return default; } public void Set(TFeature? instance) { target.Add(typeof(TFeature), instance); } public IEnumerator> GetEnumerator() { return ((IEnumerable>)target).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)target).GetEnumerator(); } } ``` #### IWebHostEnvironment ```C# //用于描述环境 public interface IWebHostEnvironment { string EnvironmentName { get; } } public class WebHostEnvironment: IWebHostEnvironment { public string EnvironmentName { get; } public WebHostEnvironment(string environmentName) { EnvironmentName = environmentName; } } public static class IWebHostEnvironmentExtensions { public static bool IsDevelopment(IWebHostEnvironment environment) { return environment.EnvironmentName == "Development"; } public static bool IsProduction(IWebHostEnvironment environment) { return environment.EnvironmentName == "Production"; } } ``` #### Kestrel ```C# //处理请求实现socket通信 internal class Kestrel { private readonly ILogger _logger; private readonly KestrelOptions _options; private readonly IWebHostEnvironment _environment; private readonly IServiceProvider _serviceProvider; public Kestrel( ILogger logger, IWebHostEnvironment environment, IServiceProvider serviceProvider, IOptions options) { _logger = logger; _options = options.Value; _environment = environment; _serviceProvider = serviceProvider; } public void Run(IApplicationBuilder applicationBuilder) { var serverSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //监听url var uri = new Uri(_options.Url); var ipaddress = IPAddress.Parse(uri.Host); var ipEndpoint = new IPEndPoint(ipaddress, uri.Port); serverSocket.Bind(ipEndpoint); serverSocket.Listen(); Logging(); Socket client; //构建管道 var app = applicationBuilder.Build(); while (true) { try { client = serverSocket.Accept(); //启动新的线程来处理请求 new Thread((obj) => { //客户端scoket Socket? client = (obj as Socket) ?? throw new ArgumentException(nameof(obj)); try { //处理请求 ReceiveClientRequestAsync(client, app).Wait(); } catch (Exception ex) { _logger.LogError(ex.Message, ex); } }).Start(client); } catch (Exception ex) { _logger.LogError(ex.Message, ex); } } } /// /// 1.一个请求一个线程(请求开始->响应结束) /// 2.一个请求一个scope(请求开始->响应结束) /// private async Task ReceiveClientRequestAsync(Socket client, RequestDelegate app) { var buffer = new byte[_options.RequestBodyMaxSize]; int cout = client.Receive(buffer); //HttpContext var package = Encoding.UTF8.GetString(buffer, 0, cout); var request = JsonSerializer.Deserialize(package) ?? throw new ArgumentException(); using (var scope = _serviceProvider.CreateScope()) { var context = new HttpContext(request, scope.ServiceProvider); await app(context);//使用管道来处理请求 var responseData = context.Response.GetBuffer(); //把数据发回客户端 if (!(responseData == null || responseData.Length==0)) { buffer = context.Response.GetBuffer(); if (buffer?.Length<=0) { buffer = Encoding.UTF8.GetBytes("Status=200"); } client.Send(buffer); } } } private void Logging() { _logger.LogInformation("Now listening on: {0}", _options.Url); _logger.LogInformation("Application started. Press Ctrl+C to shut down."); _logger.LogInformation("Hosting environment: {0}", _environment.EnvironmentName); } } //选项 public class KestrelOptions { public string Url { get; set; } public int RequestBodyMaxSize { get; set; } } ``` #### WebApplication ```C# //表示web app public class WebApplication : IApplicationBuilder, IEndpointRouteBuilder { public IServiceProvider Services { get; } public ILogger Logger { get; } public IConfiguration Configuration { get; } public IWebHostEnvironment Environment { get; } public IFeatureCollection ServerFeatures { get; } public EndpointDataSource? EndpointDataSource => ServerFeatures.Get(); private IApplicationBuilder _application; public static WebApplicationBuilder CreateBuilder() { var configuration = new ConfigurationManager(); configuration.AddEnvironmentVariables("ASPNETCORE_"); var environmentName = configuration.GetValue("Environment"); var environment = new WebHostEnvironment(environmentName); configuration.AddJsonFile("appsettings.json", true, true); configuration.AddJsonFile($"appsettings.{environmentName}.json", true, true); return new WebApplicationBuilder( environment, configuration); } public WebApplication(IServiceProvider serviceProvider) { _application = new ApplicationBuilder(); Services = serviceProvider; ServerFeatures = serviceProvider.GetRequiredService(); Configuration = serviceProvider.GetRequiredService(); Logger = serviceProvider.GetRequiredService>(); Environment = serviceProvider.GetRequiredService(); } public void Run() { var task = new TaskCompletionSource(); var kestrel = ActivatorUtilities.CreateInstance(Services); kestrel.Run(_application); task.Task.Wait(); } public void Use(Func middleware) { _application.Use(middleware); } public void Map(string pathMatch, RequestDelegate requst) { var dataSource = ServerFeatures.Get(); var endpoint = new Endpoint(requst); endpoint.Metadata.Add(new HttpMethodAttribute(pathMatch)); dataSource?.SetEndpoint(endpoint); } } //配置web app public class WebApplicationBuilder { public IWebHostEnvironment Environment { get; } public ConfigurationManager Configuration { get; } public IServiceCollection Services { get; } public ILoggingBuilder Logging { get; } public WebApplicationBuilder( IWebHostEnvironment environment, ConfigurationManager configuration) { Environment = environment; Configuration = configuration; Services = new ServiceCollection(); Logging = new LoggingBuilder(Services); } public WebApplication Build() { Services.AddLogging(); Logging.AddConsole(); Logging.AddConfiguration(Configuration.GetSection("Logging")); Services.AddSingleton(Environment); Services.AddSingleton(Configuration); Services.AddSingleton(sp => { var features = new FeatureCollection(); features.Set(new EndpointDataSource()); return features; }); Services.Configure(c => { c.Url = "http://127.0.0.1:8080"; }); return new WebApplication(Services.BuildServiceProvider()); } } internal class LoggingBuilder : ILoggingBuilder { public IServiceCollection Services { get; } public LoggingBuilder(IServiceCollection services) { Services = services; } } ``` #### RouteMiddleware ```C# //把请求路由到终结点 public class RouteMiddleware : IMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public RouteMiddleware( RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { var endpoint = context.GetEndpoint(); if (endpoint != null) { await endpoint.Delegate(context); } else { await _next(context); } } catch (Exception ex) { _logger.LogError(ex.Message, ex); } } } public static class RouteMiddlewareExtensions { public static void UseRouting(this WebApplication app) { app.Use(); } } ``` #### Endpoint ```C# //终结点 public class Endpoint { public List Metadata { get; } = new List(); public RequestDelegate Delegate { get; set; } public Endpoint(RequestDelegate handler) { Delegate = handler; } } //管理终结点 public class EndpointDataSource { private readonly List _endpoints = new List(); public IReadOnlyList Endpoints =>_endpoints; public void SetEndpoint(Endpoint endpoint) { _endpoints.Add(endpoint); } } //根据请求匹配终结点 public static class HttpContextExtensions { public static Endpoint? GetEndpoint(this HttpContext context) { var features = context.RequestServices.GetRequiredService(); var dataSource = features.Get(); if (dataSource == null) { return null; } foreach (var item in dataSource.Endpoints) { HttpMethodAttribute? httpMethod = item.Metadata.FirstOrDefault(a => a is HttpMethodAttribute) as HttpMethodAttribute; if (httpMethod == null) { continue; } if (httpMethod.Method != null && httpMethod.Method == context.Request.Method && httpMethod.Template == context.Request.Url) { return item; } if (httpMethod.Method == null && httpMethod.Template == context.Request.Url) { return item; } } return null; } public static void SetEndpoint(this HttpContext context, Endpoint endpoint) { var features = context.RequestServices.GetRequiredService(); var dataSource = features.Get(); dataSource?.SetEndpoint(endpoint); } } //注解终结点的信息 public class HttpMethodAttribute: Attribute { public string Template { get; } public string? Method { get; } public HttpMethodAttribute(string template, string method) { Template = template; Method = method; } public HttpMethodAttribute(string template) { Template = template; } } public class HttpGetAttribute : HttpMethodAttribute { public HttpGetAttribute(string template) :base(template,HttpMethods.Get) { } } public class HttpPostAttribute : HttpMethodAttribute { public HttpPostAttribute(string template) : base(template, HttpMethods.Post) { } } public static class HttpMethods { public static string Post = "Post"; public static string Get = "Get"; } public interface IEndpointRouteBuilder { EndpointDataSource? EndpointDataSource { get; } void Map(string pathMatch, RequestDelegate requst); } //构建终结点 public static class IEndpointRouteBuilderExtensions { public static void MapGet(this IEndpointRouteBuilder endpoints, string pathMatch, RequestDelegate requst) { var endpoint = new Endpoint(requst); endpoint.Metadata.Add(new HttpGetAttribute(pathMatch)); endpoints.EndpointDataSource?.SetEndpoint(endpoint); } public static void MapPost(this IEndpointRouteBuilder endpoints, string pathMatch, RequestDelegate requst) { var endpoint = new Endpoint(requst); endpoint.Metadata.Add(new HttpPostAttribute(pathMatch)); endpoints.EndpointDataSource?.SetEndpoint(endpoint); } } ``` #### ControllerBase ```C# public abstract class ControllerBase { public HttpContext HttpContext { get; internal set; } public HttpRequest Request => HttpContext.Request; public HttpResponse Response => HttpContext.Response; } //把控制器处理成终结点 public static class IMvcEndpointRouteBuilderExtensions { public static void MapControllers(this WebApplication app) { var endpoints = app.ServerFeatures.Get(); if (endpoints == null) { return; } var assembly = Assembly.GetExecutingAssembly(); var controllers = assembly.GetTypes().Where(a => a.IsSubclassOf(typeof(ControllerBase))); foreach (var item in controllers) { var methods = item.GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (var method in methods) { var httpMethod = method.GetCustomAttributes() .Where(a => a is HttpMethodAttribute).FirstOrDefault(); if (httpMethod==null) { continue; } RequestDelegate handler = context => { var controller = ActivatorUtilities.CreateInstance(context.RequestServices, item); if (controller is ControllerBase controllerBase) { controllerBase.HttpContext = context; } //modelBinder method.Invoke(controller, new object[0]); return Task.CompletedTask; }; var endpint = new Endpoint(handler); endpint.Metadata.Add(httpMethod); endpoints.SetEndpoint(endpint); } } app.UseRouting(); } } ``` ## HttpContext ### Request 了解Reequest对象 ### Response 了解Response对象 ### Cookie 了解Cookie对象 ### Session 了解session对象 ## MVC ### Model 了解模型绑定 ### View 了解视图的原理以及动态编译 ### Controller 了解控制器 ### Filter 了解过滤器 ## Middleware ## HttpClient ## Cache 了解缓存的标准接口 ## ObjectBool 了解池技术 ## 线程及安全 ### 安全 ### 读锁 ### 写锁 ## Mediator ## EntityFramework ### 了解ef的基本使用 ### 了解实体配置及迁移 ### 线程的常用类 ## 传统分层设计 ### 三层架构及意义 ## DDD ### 聚合 ### 领域模型 ### 领域服务 ### 充血模型 ## CQRS ### 命令模式 ### 事件模式 ### 查询分离 ## 事务管理 ## Expression ### 解析 ### 构建 ## Quartz ### 学会基本的使用 ### 高可用性及持久化 ## Nuget ### 了解原包 ### 了解依赖 ### 掌握多标准库 ## 身份认证 ### cookie方案 ### jwt方案 ### 自定义方案 ### Soul.IdentityServer ## 资源授权 ### 内置授权机制 ### 自定义授权组件 ## 微服务基本架构 ### 分布式Id ### 统一身份认证 ### nginx网关 ### 服务调用 ### 分布式事务 ### 分布式锁 ## 云原生 ### Docker基本使用 ### K8s实战部署 ### Helm基本使用 ### Dapr基本使用 ###