AspNetCore基础课程
我打算录制一个基础课程从原理、思维、实现等角度详细讲解。IOC、Options、Configuration、Logging、AOP、管道中间件、路由终结点,mvc的原理。掌握这些基础知识之后,我会基于这些技术+scoket手写一个aspnetcore。用以窥探aspnetcore的内部是如何运行的。
下面我详细解释两点基础技术IOC和AOP,大家感受一下我授课的风格。
注意基础课里讲到的知识,在整个net平台几乎都适用,及支持aspnet,winform,wpf。
视频课程
AspNetCore企业级开发《公开课》_哔哩哔哩_bilibili
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容器根据反射得到一个类的依赖关系,自动帮你载入依赖项的过程
服务注册
-
万能公式
//需要安装:Microsoft.Extensions.DependencyInjection //创建IServiceCollection实列 IServiceCollection services = new ServiceCollection(); //由于IServiceCollection实现了IList<ServiceDescriptor>接口 //因此下面是一个万能公式,其它的都是扩展方法 services.Add(new ServiceDescriptor(typeof(IConnection),typeof(SqlConnection),ServiceLifetime.Singleton));
-
泛型接口
//泛型接口需要提前知道类型 services.AddSingleton<IDbConnection, SqlConnection>();
-
反射接口
//反射的方式在编写框架时十分有用,无反射无框架 services.AddSingleton(typeof(IDbConnection), typeof(SqlConnection);
-
委托方式
//当我们构建的对象需要编写逻辑时,委托方式十分有用 services.AddSingleton<IDbConnection, SqlConnection>(); //低级用法 //假设DbContext依赖IDbConnection,并且需要一个name services.AddSingleton(sp => { var connection = sp.GetRequiredService<IDbConnection>(); return new DbContext(connection, "c1"); }); //高级用法 //委托方式在注册的同时还能进行预解析 services.AddSingleton(sp => { //sp是一个IServiceProvider的实列 return ActivatorUtilities.CreateInstance<DbContext>(sp,"c1"); });
构建容器
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = true,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
服务解析
//如果同一个服务类型,注册多个实现,那么默认获取最后一个实现。
services.AddSingleton<IDbConnection, SqlConnection>();
services.AddSingleton<IDbConnection, MySqlConnection>();
IServiceProvider container = services.BuildServiceProvider();
//如果服务未注册,返回null
IDbConnection? connection = container.GetService<IDbConnection>();
//服务不存在讲引发异常
IDbConnection connection = container.GetRequiredService<IDbConnection>();
//获取IDbConnection所有实现
IEnumerable<IDbConnection> connections = container.GetRequiredServices<IDbConnection>();
//假设DbContext依赖IDbConnection,并且需要一个name,但是容器没有注册DbContext
var context = ActivatorUtilities.CreateInstance<DbContext>(container,"c1");
工厂模式
通过上面的演示我们发现IOC有一个弊端,就是他是通过服务类型的解析服务的。有些情况下我们需要通过命名的方式来解析服务。此时可以使用工厂模式
public class Connection
{
}
public class ConnectionFactory
{
private IServiceProvider _serviceProvider;
//value:可以写一个类来描述连接字符串和类型
private Dictionary<string, Type> _connections;
public ConnectionFactory(IServiceProvider provider, Dictionary<string, Type> connections)
{
_serviceProvider = provider;
_connections = connections;
}
public Connection? Create(string name)
{
if (_connections.TryGetValue(name, out Type? connectionType))
{
return _serviceProvider.GetService(connectionType) as Connection;
}
return default;
}
}
//测试
services.AddSingleton(sp =>
{
var connections = new Dictionary<string, Type>();
connections.Add("s1", typeof(MysqlConnection));
connections.Add("s2", typeof(MysqlConnection));
return new ConnectionFactory(sp, connections);
});
IServiceProvider container = services.BuildServiceProvider();
var factory = container.GetService<ConnectionFactory>();
var connection1 = factory.Create("s1");
var connection2 = factory.Create("s2");
生命周期
我们需要会搭建测试案例,来验证是否是同一个实列,以及释放问题。
public class A : IDisposable
{
public string ID { get; }
public A()
{
ID = Guid.NewGuid().ToString();
}
public void Dispose()
{
Console.WriteLine(ID + ":已释放...");
}
}
//你可以测试其他生命周期
services.AddScoped<A>();
//根容器:通过Debug模式查看container可以看到一个属性IsRootScope用来标记它是否是根容器
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = false,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
//a1:通过根容器创建,需要设ValidateScopes为false(危险)
var a1 = container.GetRequiredService<A>();
var a2 = container.GetRequiredService<A>();
using(var scope = rootContainer.CreateScope())
{
//a2:通过子容器创建(合法)
var a3 = scope.ServiceProvider.GetRequiredService<A>();
var a4 = scope.ServiceProvider.GetRequiredService<A>();
Console.WriteLine(a1.ID);
Console.WriteLine(a2.ID);
Console.WriteLine(a3.ID);
Console.WriteLine(a4.ID);
}
Console.ReadLine();
通过修改A服务注册的生命周期我们可以得到一下结论。
Singleton:无论通过根容器还是子容器,获取的都是同一实列,而且不会执行释放(除非释放根容器)。
Scoped:同一scope获取的都是同一实列,不同的scope获取的实列不同,而且不会执行释放(除非释放根容器)。scope释放会释放由它解析出来的所有实列,如果并执行Dispose方法。
Transient:无论是否同一scope获取的实列都不同,scope释放会释放所有的实列。
注意:ServiceProvider会记录由它创建的所有实列,如果释放scope,则会释放所有(单实列除外)由它创建的实列。
Scope范围:scope的范围有多大取决于你何时创建何时释放。从创建到释放就是他的生命周期。
//按时间5s之后释放
public class A : IDisposable
{
public string ID { get; } = Guid.NewGuid().ToString();
public void Dispose()
{
Console.WriteLine(ID + ":已释放...");
}
}
IServiceProvider container = services.BuildServiceProvider();
var scope = rootContainer.CreateScope();
var a1 = container.GetRequiredService<A>();
Thread.Sleep(5 * 1000);
scope.Dispose();
AOP
基础知识
假设这是一个U型管道,污水水从一端流入,另一端流出。
现在我们要对污水进行过滤,A负责过滤B负责消毒。显然有四个处理点,他们的顺序分别是:1->2->3->4。其中1,4是A过滤器的处理点,2,3是B过滤器的处理点。显然3个过滤器就有6个处理点。我们可以随意调整A,B过滤器的顺序,可随意插拔。这就是AOP的思想。
执行顺序:先进后出(栈)
执行点数:过滤器数 * 2
AOP是对OOP的一种补充,即面向切面编程,一种编程思想。我们管A,B为切面。1~4为切入点。AOP的优势是面向切面编程,每个切面负责独立的系统逻辑,降低代码的复杂度,提高代码的复用率。可以随意调整顺序,随意插拔。用于对业务逻辑进行增强。面向切面编程可以使得系统逻辑和业务逻辑进行分离。
系统逻辑:比如身份认证,异常处理,参数校验
业务逻辑:就是我们真正关心不得不写的逻辑。
public class Demo
{
public void A()
{
Console.WriteLine(1);
B();
Console.WriteLine(4);
}
public void B()
{
Console.WriteLine(2);
Console.WriteLine(3);
}
}
//这段代码没有实际意义,但是它展示了函数调用的执行过程。(先进后出)
静态代理
假设我们需要实现一个IList接口,我们知道IList接口有很多方法,实现成本非常高。我们可以通过代理模式来实现
代理模式可以降低实现的成本,还可以对目标对象进行加强。代理者不需要实现具体的业务逻辑,只需要编写加强逻辑即可。
class MyCollection : IEnumerable<object>
{
private IEnumerable<object> _target;
public MyCollection(IEnumerable<object> target)
{
_target = target;
}
public IEnumerator<object> GetEnumerator()
{
//编写加强逻辑比如打印
Console.WriteLine("调用迭代器了");
//通过target来实现
return _target.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_target).GetEnumerator();
}
}
var list = new List<object>();
//可以看到MyCollection对list进行了代理,加强了GetEnumerator函数
var collection = new MyCollection(list);
//此时GetEnumerator就会被加强,返回list的迭代器。
var it = collection.GetEnumerator();
我们可以通过静态代理来实现链式调用,完成污水处理问题。
public interface IWater
{
void Invoke();
}
public class Water : IWater
{
public void Invoke()
{
//业务逻辑
Console.WriteLine("水已经净化了");
}
}
//实现目标对象的接口IWater
public class WaterProxy1 : IWater
{
private readonly IWater _target;
public WaterProxy1(IWater target)
{
_target = target;
}
public void Invoke()
{
Console.WriteLine("开始消毒杀菌");//系统逻辑
_target = target;
Console.WriteLine("完成消毒杀菌");//系统逻辑
}
}
public class WaterProxy2 : IWater
{
private readonly IWater _target;
public WaterProxy2(IWater target)
{
_target = target;
}
public void Invoke()
{
Console.WriteLine("开始去除杂质");//系统逻辑
_target = target;
Console.WriteLine("完成去除杂质");//系统逻辑
}
}
//此时是先去除杂质后在消毒
//p1的target是Water,p2的target是p1
var target = new Water();
var p1 = new WaterProxy1(target);
var p2 = new WaterProxy2(p1);
p2.Invoke();
//此时是先消毒后在去除杂质
//p2的target是Water,p1的target是p2
var target = new Water();
var p2 = new WaterProxy2(target);
var p1 = new WaterProxy1(p2);
p2.Invoke();
可以看到系统逻辑和业务逻辑进行了分离,系统逻辑写到了不同的切面。切面之间何以随意组合,增减。这就是AOP思想的一种呈现方式。代码服用度很高,可以代理所有的IWater的实现。(假设Mercury也实现了IWater接口,那么WaterProxy1和WaterProxy2也能对他进行增强)
静态代理的本质是子类继承父类,或者实现接口,对目标对象进行增强。
静态代理的弊端是只能实现一个接口(标准),无法代理其他类型的实列。他的切面的可复用率有限,限定在它实现的接口。
动态代理
动态代理可以通过Castle.Core来实现。我们说静态代理和动态代理的区别是,静态代理在代码编译之前就已经确立的代理关系。而动态代理的原来是,在编译之后,运行时通过Emit来动态创建目标对象的子类,或者实现目标对象的接口。把拦截器织入到动态生成的类中,这里的拦截器可以织入到任意的实现类中。(Emit技术可以在运行时生成一个class,大家可以通过打印castle.core返回的实列的类名来进行验证)
动态代理和静态代理的本质都是继承或者实现,但是静态代理是需要手动编写代理类,而动态代理由框架动态生成代理类。
public interface IWater
{
void Invoke()
}
public class Water()
{
public void Invoke()
{
//业务逻辑
Console.WriteLine("水已经净化了");
}
}
//拦截器-切面
public Interceptor1 : IInterceptor
{
void Intercept(IInvocation invocation)
{
Console.WriteLine("开始去除杂质");//系统逻辑
invocation.Proceed();
Console.WriteLine("完成去除杂质");//系统逻辑
}
}
var generator = new ProxyGenerator();
var target = new Water();
var proxy = generator.CreateInterfaceProxyWithTarget<IWater>(target,new Interceptor1());
Console.WriteLine(proxy.GetType().FullName);//可以看到这个类并不是我们生成的
proxy.Invoke();
委托方式
这是aspnetcore管道的核心代码
public interface IWater
{
void Invoke();
}
public class Water : IWater
{
public void Invoke()
{
//业务逻辑
Console.WriteLine("水已经净化了");
}
}
//定义一个委托
public delegate void WaterDelegate(IWater water);
public class ApplicationBuilder
{
private readonly List<Func<WaterDelegate, WaterDelegate>> _components = n
public void Use(Func<WaterDelegate, WaterDelegate> middleware)
{
_components.Add(middleware);
}
public WaterDelegate Build()
{
//负责兜底
WaterDelegate app = c =>
{
throw new InvalidOperationException("无效的管道");
};
for (int i = _components.Count - 1; i > -1; i--)
{
app = _components[i](app);//完成嵌套
}
return app;
}
}
测试
var builder = new ApplicationBuilder();
//过滤器1
builder.Use(next =>
{
return water =>
{
Console.WriteLine("开始去污");
next(water);
Console.WriteLine("完成去污");
};
});
//过滤器2
builder.Use(next =>
{
return water =>
{
Console.WriteLine("开始消毒");
next(water);
Console.WriteLine("完成消毒");
};
});
//构建管道
var app = builder.Build();
var target = new Water();
//开始处理
app.Invoke(target);
接口方式
public class HttpContext
{
}
//链路器
public interface IChain
{
Task NextAsync();
}
//用于执行filter
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);
}
}
//用于执行servlet
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<IFilter> _filters = new List<IFilter>();
public void AddFilter(IFilter filter)
{
_filters.Add(filter);
}
public void Execute(HttpContext context, IServlet servlet)
{
//自行处理filter为空的情况,就是直接执行serlvet就好了
var stack = new Stack<IFilter>(_filters);
var filter = stack.Pop();
var chain = GetFilterChain(context, servlet, stack);
filter.InvokeAsync(context, chain);
}
//构建链路器(递归算法)
private IChain GetFilterChain(HttpContext context, IServlet servlet, Stack<IFilter> filters)
{
if (filters.Any())
{
var filter = filters.Pop();
var next = GetFilterChain(context, servlet, filters);
return new FilterChain(filter, context, next);
}
else
{
return new ServletChain(servlet, context);
}
}
}
测试
var host = new WebHost();
host.AddFilter(new Filter1());
host.AddFilter(new Filter2());
var context = new HttpContext();
host.Execute(context);
标签:Console,target,AspNetCore,基础,课程,WriteLine,var,new,public
From: https://www.cnblogs.com/chaeyeon/p/17120832.html