❡ 语法篇
❡ 基础背景知识
c# 编写的应用程序必须放置于.NET 环境中才能正常允许。C# 代码最终会被编译为中间语言 "(MSIL)"。
类库是以.dll 结尾,不能直接运行,而启动项目是以.exe 结尾可以直接运行。
1 |
|
主项目也叫启动项目,可以通过该设置方式设置启动项目。
❡ 基本语法
❡ 面向过程
❡ Debug 类
1 |
|
❡ 变量与常量
1 |
|
❡ 注释
summary 注释:ctrl+m
多行注释:ctrl+/
单行注释:ctrl+k
❡ 小驼峰和大驼峰
1 |
|
❡ As 和 is
1 |
|
❡ 类型转换
1 |
|
❡ 自定义转换
implicit 和 explicit 不能重复
❡ implicit
1 |
|
❡ explicit
1 |
|
❡ 创建对象的三种方式
1 |
|
❡ 方法
1 |
|
❡ Out 、ref、使用元组组合多个返回值
1 |
|
❡ Params
1 |
|
❡ Switch
1 |
|
❡ 字符串 @输出转义符、字符和字符串转换
1 |
|
❡ 字符串 Null
1 |
|
❡ typeof 和 sizeof
1 |
|
❡ ?和??(合并运算符)
1 |
|
❡ 字符串
1 |
|
❡ 区域性相关的信息
1 |
|
❡ 字符串格式化
1 |
|
❡ 字符串复合格式化
1 |
|
❡ 生成随机数
1 |
|
❡ 常见的时间计算
1 |
|
❡ 日期格式化
1 |
|
❡ 集合
1 |
|
❡ HashSet
❡ IsSubsetOf–子集
❡ IsSupersetOf–父集
❡ Overlaps–交集
❡ UnionWith–并集
❡ RemoveWhere(new Predicate(xxx))
1 |
|
❡ 字典
❡ HashTable 与 Dictionary
1 |
|
❡ 自定义排序
❡ 两种方式
Comparer
IComparer
1 |
|
❡ 控制台应用程序
❡ 获取文本信息
1 |
|
❡ 获取键盘输入
❡ Read
Read (), 每次只读入一个字符,Ctrl+Z 返回 - 1
❡ ReadKey
ReadKey (),读取用户输入的字符
ReadKey 返回 ConsoleKeyInfo 实例, 包含三个属性:Key(返回 ConsoleKey 枚举)、keyChar(直接返回 Unicode 字符)、Modifiers (返回 ConsoleModifiers 枚举值,表示用户是否按下了 Ctrl、Alt、Shift)
❡ ReadLine
ReadLine () 如果按下 Ctrl+Z+Enter 键会返回 Null
❡ 命令行参数
❡ 两种方式
1 |
|
❡ 控制台窗口的外观
❡ 三个属性
❡ Title
❡ BackgroundColor
❡ ForegroundColor–文本颜色也就是前置颜色
1 |
|
❡ 控制台窗口的大小和位置
❡ 注意点:
缓冲区大小比窗口大小要大,要先设置窗口大小为 1,1 之后再重新设置。
WindowTop 以行为单位,WindowLeft 以列为单位。
ConsoleKey.RightArrow// 右边
Console.WindowLeft < (Console.BufferWidth - Console.WindowWidth)
Console.SetWindowPosition(Console.WindowLeft + 1, Console.WindowTop);
1 |
|
响应 CancelKeyPress 事件
1 |
|
❡ 数组
❡ 定义方式
1 |
|
❡ 复制数组
1 |
|
❡ 查找元素
❡ 查找元素索引
1 |
|
❡ 查找元素本身
1 |
|
❡ 反转数组
1 |
|
❡ 更改数组大小
1 |
|
❡ 多维数组
1 |
|
❡ 交错数组 / 数组的数组 / 嵌套数组
1 |
|
❡ Array 类
1 |
|
❡ 动态数组
1 |
|
❡ 结构体
1 |
|
❡ 枚举
1 |
|
❡ 常用 API
❡ StringBuilder
1 |
|
❡ 正则表达式(需要再次学习)
1 |
|
❡ 格式化字符串
1 |
|
❡ 补充知识
❡ 泛型
1 |
|
❡ 顶级语句
❡ 注意点:
- 一个项目中,只允许一个代码文件使用顶层语句。
- 由于顶层语句在编译时自动生成入口点方法 Main,所以在同一个项目中不能再选择其他入口点方法(即编译器选项中不能使用 /main 或 - main 开头)
1 |
|
❡ record
record 与 class 的区别在于相等比较的计算不同,如果相同的属性 class 在相等比较运算符上会给出 false, 而 record 会给出 true.
1 |
|
❡ Global
global using System;
❡ 异常处理
1 |
|
❡ 命名空间
作用:避免名称冲突
不是本项目的需要先引用目标命名空间所在程序集
❡ NameSpace
1 |
|
❡ using
起别名
1
2using p1 = System.Math;
var v1 = p1.PI;引入命名空间
释放空间
静态成员访问
1
2
3
4
5using static System.Math;
var v1 = PI;
using static System.Console;
WriteLine();//只能放为静态成员,实例成员无法访问
❡ 预处理器指令
#region #endregion
#warning 和 #error
#lines
#pragma
❡ 文件的输入和输出
1 |
|
❡ 不安全代码
❡ 设置
❡ 使用指针检索数据值
1 |
|
❡ 传递指针作为方法的参数
1 |
|
❡ 使用指针访问数组元素
fixed (int *ptr = list), 指针变量在数组中内存固定,其余地方不固定,需要使用 fixed () 来固定
C# 中声明的变量在内存中的存储受垃圾回收器管理;因此一个变量(例如一个大数组)有可能在运行过程中被移动到内存中的其他位置。如果一个变量的内存地址会变化,那么指针也就没有意义了。
1 |
|
❡ 面向对象
❡ 类
❡ 基础使用
1 |
|
❡ 抽象类
1 |
|
❡ 密封类
1 |
|
❡ 静态类
❡ 单例模式
1 |
|
❡ 静态构造方法
1 |
|
❡ 只读字段
1 |
|
❡ 字段和属性
1 |
|
❡ Init 初始化器
1 |
|
❡ 封装
❡ Protected
1 |
|
❡ Internal
一个项目就是一个程序集。
可以通过创建类库,保护级别为 internal,然后在主函数的解决方案那边添加类库的引用,从而验证是错误的。
1 |
|
❡ Protected Internal
1 |
|
❡ 多态
❡ virtual 和 override、new
1 |
|
❡ 静态多态
❡ 方法重载
1 |
|
❡ 运算符重载
1 |
|
❡ 动态多态
1 |
|
❡ 继承
派生类的可访问性不应该比基类高。这样会避免访问冲突。
❡ 定义接口
1 |
|
❡ 接口继承
1 |
|
❡ 显示接口
1 |
|
❡ 高级语法
❡ 单元测试
❡ 安装包以及引入的包
❡ 安装包
- NUnit
- Microsoft.NET.Test.Sdk
- NUnit3TestAdapter
❡ 引入包
using NUnit.Framework;
❡ 项目属性修改,不然会显示 Main 入口不对
❡ 基础使用
onetimesetup 不输出结果,断言.that (4,Is.equalto (2+3)) 错误会显示结果,正确没有结果。
test 的话需要先运行所有测试,之后才会有√进行单独的单元测试。
1 |
|
❡ 断言
作用:检验代码的执行是否符合预期的结果。
1 |
|
❡ 使用日志文件
1 |
|
❡ 反射
GetMethod、GetConstructor 都与 invoke 有关
var Obj = assembly.CreateInstance(「MyStudyProcess_C#Senior.MyTest」,false);// 命名空间。类名
1 |
|
1 |
|
1 |
|
❡ 扩展知识
❡ 扩展方法
自定义扩展方法
1、静态类
2、静态方法
3、参数列表前加上 this
扩展方法向现有类型 “添加” 方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。
扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。
1 |
|
❡ 特性
❡ 自定义特性
1 |
|
❡ 预定义特性
.Net 框架提供了三种预定义特性:
- AttributeUsage
- Conditional–条件,#define xxx
- Obsolete–可以用旧的方法
1 |
|
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
1 |
|
❡ 索引器
1 |
|
❡ 序列化
❡ 二进制序列化
1 |
|
1 |
|
❡ XML 序列化
1 |
|
❡ 自定义 xml 文档的节点
1 |
|
1 |
|
❡ 数据协定
数据交互过程对象不一定相同类型,必须遵循数据协定来定义。
1 |
|
❡ JSon 序列化
1 |
|
❡ JsonSerializer
1 |
|
❡ 自定义转换器
1 |
|
❡ 自定义属性名称
1 |
|
在线 JSON 校验格式化工具(Be JSON)
1 |
|
❡ 泛型
由于泛型委托 xxx
public delegate TResult Func<in T1,in T2,out TResult>(T1 arg1,T2 arg2);
❡ 协变
out 只能用于接口
父类 = 子类
1 |
|
❡ 逆变
1 |
|
❡ 泛型的应用
❡ 手写 ORM 框架(很难)
ORM 框架,对象关系映射。
- 从老师提供 utils 文件夹中将 DbHelper 拷贝至当前你的项目中
- nuget 引用:
- 1:Microsoft.Extensions.Configuration,
- 2:System.Data.SqlClient
- 封装 ORM 框架
1 |
|
❡ DataTable 转 List
DataTable 转换成 List
1 |
|
❡ 泛型集合
❡ 存在的问题
1 |
|
❡ 性能对比
❡ 非泛型集合性能
1 |
|
❡ 泛型集合性能
1 |
|
❡ I/O 文件流
❡ Stream 流
1 |
|
❡ 内存映射文件
内存映射文件可以单独存放于内存中,也可以把磁盘上的文件映射为内存文件来进行处理.
1 |
|
1 |
|
❡ 独立存储
独立存储也叫隔离存储,是一个由操作系统提供的、可以存储文件和目录的一个特殊区域。
1 |
|
❡ 委托
- 1、委托的本质就是一个 Sealed 类
- 2、继承自 System.MulticastDelegate:Delegate
- 3、委托里面内置了 3 个方法:Invoke (),BeginInvoke (),EndInvoke ()。
重要结论
- 参数列表和返回值称为方法签名
- 委托的签名要与方法的签名保持一致
1 |
|
❡ 弃元、匿名方法与 Lambda 表达式
❡ 弃元
1 |
|
❡ 匿名方法
1 |
|
❡ Lambda 表达式
- Lambda 表达式是一个匿名函数,用它可以高效简化代码,常用作委托,回调
- Lambda 表达式都使用运算符 => ,所以当你见到这个符号,基本上就是一个 Lambda 表达式
- Lambda 运算符的左边是输入参数 (), => ,右边是表达式或语句块
- Lambda 表达式,是可以访问到外部变量的
1 |
|
❡ 系统内置委托
❡ Action
Action 无返回值的内置委托,有 16 个重载方法:
- Action 声明无参数委托
- Action 声明有一个参数委托
- Action 声明有 2 个参数委托
- Action 声明有 3 个参数委托
- Action 委托输入参数个数最多 16 个。
备注
1、它有 16 重载方法
2、有 16 个输入参数
3、 Action 的返回类型是 Void
1 |
|
❡ Func<T,TResult>
Func 有返回值的内置委托,有 17 个重载方法:
- 1、Func
- 2、Func<T,TResult>
- 3、 Func<T1,T2,TResult>
- 4、 Func<T1,T2,T3,TResult>
- 5 、Func<T1,…,TResult>
备注
1、它有 17 个重载方法
2、有 T1 到 T16 个输入参数
3、最后一个参数 Tresult 代表返回类型
1 |
|
❡ 委托异步调用
特点:
- 异步调用,即在线程池分配的子线程中执行委托,因此执行时不会阻塞调用委托的线程,该线程在 调用后不等委托结束继续向下执行。
- 委托结束时,如果有返回值,子线程将返回值传递给调用线程;
- 委托方法执行结束后,如果有回调函数,则在子线程中继续执行回调函数,直到回调函数结束委托 才结束。
1 |
|
但是上述代码会抛出异常 System.PlatformNotSupportedException : Operation is not supported on this platform.
原因如下:
异步编程模型 (APM)(使用 IAsyncResult 和 BeginInvoke) 不再是异步调用的优选方法。从.NET Framework 4.5 开始,推荐的异步模型是基于任务的异步模式 (TAP)。因此,而且由于异步委托的 实现取决于远程处理,但.NET Core 中 是不存在的功能,BeginInvoke 和 EndInvoke 委托调用不支 持 .NET Core
❡ 使用任务实现异步调用
1 |
|
❡ event 事件
1 |
|
❡ 事件的本质
通过反射我们可以得出结果:
- IsClass: true
- 父类是 MulticastDelegate,成员方法 Invoke,BeginInvoke,EndInvoke
- IsSealed: true
- 结论:事件是一个委托,并且还是一个特殊的密封类。
❡ 事件与委托的区别
- 事件只能在类的内部进行触发,不能在类的外部进行触发。而委托在类的内部和外部都可触发;
- 委托一般用于回调,而事件一般用于外部接口。在观察者模式中,被观察者可在内部声明一个事件
- 作为外部观察者注册的接口。
- 事件只能通过 +=,-= 方式 绑定 / 解绑方式
- 事件是一个特殊的委托,查看反编译工具之后的代码,发现事件是一个 private 委托
❡ 观察者模式
❡ 错误写法
1 |
|
❡ 正确写法
Observer 设计模式中主要包括如下两类对象:
Subject:监视对象(被观察者),它往往包含着其他对象所感兴趣的内容。在本范例中,热水器
就是一个监视对象,它包含的其他对象所感兴趣的内容,就是 temprature 字段,当这个字段的值
快到 100 时,会不断把数据发给监视它的对象。
Observer:观察者,它监视 Subject,当 Subject 中的某件事发生的时候,会告知 Observer,而
Observer 则会采取相应的行动。在本范例中,Observer 有警报器和显示器,它们采取的行动分别
是发出警报和显示水温。
在本例中,事情发生的顺序应该是这样的:
- 警报器和显示器告诉热水器,它对它的温度比较感兴趣 (注册)。
- 热水器知道后保留对警报器和显示器的引用。
- 热水器进行烧水这一动作,当水温超过 95 度时,通过对警报器和显示器的引用,自动调用警报器
的 MakeAlert () 方法、显示器的 ShowMsg () 方法。
类似这样的例子是很多的,GOF (设计模式) 对它进行了抽象,称为 Observer 设计模式:Observer**** 设计
模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对
象会被自动告知并更新。Observer 模式是一种松耦合的设计模式。
观察者模式,也叫发布订阅模式
1 |
|
❡ 表达式树
❡ Expression 类
提供一种基类,表示表达式树节点的类派生自该基类。 它还包含用来创建各种节点类型 (表达式类型) 的
static 工厂方法。 这是一个 abstract 类。
❡ 应用场景:
- 自定义 ORM 框架查询条件 (用来检查用户信息合法性)
- 规则引擎
❡ 常用表达式
❡ 1. 常量表达式 **-ConstantExpression**
1 |
|
❡ 2. 参数表达式 **-ParameterExpression**
1 |
|
❡ 3. 二叉(二元)表达式 **-BinaryExpression**
1 |
|
❡ 4. Lambda**** 表达式
1 |
|
❡ 5. 成员表达式 **-MemberExpression**
1 |
|
❡ 6. 方法表达式 **-MethodCallExpression**
1 |
|
❡ 7. 成员初始化表达式 **-MemberBinding**
1 |
|
❡ 表达式实战(很难)
❡ MyTest
1 |
|
❡ 1. AutoMap 自动映射框架
1 |
|
方法测试
1 |
|
❡ 2. ORM 条件表达式
EF 框架中 一般可以将 Expression 表达式作为查询条件,但如果是自定义的 ORM 框架,我们该如何将
Expression 表达式生成可执行的 SQL 呢?
- 新建 Asp.net Mvc 项目,搭建 EF 框架
(1)Nuget 安装: microsoft.entityframeworkcore,
microsoft.entityframeworkcore.sqlserver
1 |
|
在 Program 中加下如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14var builder = WebApplication.CreateBuilder(args);
ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) => category ==
DbLoggerCategory.Database.Command.Name && level ==
LogLevel.Information).AddConsole(); // 创建打印控制台的日志
});
builder.Services.AddControllersWithViews();
builder.Services.AddScoped<MyDbContext>();
builder.Services.AddDbContext<MyDbContext>(p =>
{
p.UseSqlServer(builder.Configuration.GetConnectionString("Sql"));
p.UseLoggerFactory(efLogger); // 打印EF执行的SQL
});appsettings.json 配置如下:
1
2
3
4
5
6
7
8
9
10
11
12{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Sql": "server=.;uid=sa;pwd=123456;database=unit21"
}
}自定义类 CustomerVisitor 继承至 ExpressionVisitor
假设你要执行的表达式如下:
1
2Expression<Func<Product, Boolean>> exp = p =>
p.Id > 0 && p.PName.Contains("vip");ExpressionVisitor 可以解析 Expression 表达式中每一个节点的执行过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107public class CustomerVisitor : ExpressionVisitor
{
// 因为表达式是从右至左进行解析
// 先进去的条件后面生成,使用栈刚好可以满足条件
private Stack<string> _condition = new ();
public String GetCondition()
{
string condition = string.Concat(_condition.ToArray());
_condition.Clear();
return condition;
}
// ([p].[Id] > 0) AND ([p].[PName] LIKE N'%vip%')
public override Expression? Visit(Expression? node)
{
return base.Visit(node);
}
// 解析二元表达式,
protected override Expression VisitBinary(BinaryExpression node)
{
_condition.Push(")");
// 表达式是从右到左进行解析
base.Visit(node.Right);
_condition.Push(GetOperation(node.NodeType));
base.Visit(node.Left);
_condition.Push("(");
return node;
}
// 常量表达式
protected override Expression VisitConstant(ConstantExpression
node)
{
var nodeValue = node.Value;
if (nodeValue!=null)
{
if (nodeValue is string or DateTime)
{
_condition.Push($"'{node.Value.ToString().Replace("'","")}'");
}
else
{
_condition.Push(nodeValue.ToString());
}
}
return node;
}
// 解析方法
protected override Expression VisitMethodCall(MethodCallExpression node)
{
base.Visit(node.Object); // 方法调用者,如
p.PName.Contains("vip") 代表着 p.PName 就是方法的调用者
base.Visit(node.Arguments[0]); // 参数:本例中代表:vip
var arg = _condition.Pop(); // 拿出参数的值
var member = _condition.Pop(); // 属性名
switch (node.Method.Name)
{
case "Contains":
_condition.Push($"{member} like '% {arg.Replace("'","")}%'");
break;
case "Equals":
_condition.Push($"=");
base.Visit(node.Arguments[0]);
break;
default:
throw new NotSupportedException($"暂不支持{node.Method.Name}");
}
return node;
}
// 解析参数
protected override Expression
VisitParameter(ParameterExpression node)
{
return base.VisitParameter(node);
}
// 解析成员
protected override Expression VisitMember(MemberExpression node)
{
_condition.Push($"[{node.Member.Name}]");
return node;
}
/// <summary>
/// 将节点类型 转换为 SQL 运算符
/// </summary>
/// <param name="eType"></param>
/// <returns></returns>
private string GetOperation(ExpressionType eType)
{
return eType switch
{
ExpressionType.OrElse => " OR ",
ExpressionType.Or => "|",
ExpressionType.AndAlso => " AND ",
ExpressionType.And => "&",
ExpressionType.GreaterThan => ">",
ExpressionType.GreaterThanOrEqual => ">=",
ExpressionType.LessThan => "<",
ExpressionType.LessThanOrEqual => "<=",
ExpressionType.NotEqual => "<>",
ExpressionType.Add => "+",
ExpressionType.Subtract => "-",
ExpressionType.Multiply => "*",
ExpressionType.Divide => "/",
ExpressionType.Modulo => "%",
ExpressionType.Equal => "=",
_=>""
};
}
}测试调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49Expression<Func<Product, Boolean>> exp = p =>
p.Id > 0 && p.PName.Contains("vip");
CustomerVisitor visitor = new CustomerVisitor();
visitor.Visit(exp);
var condition = visitor.GetCondition
using System.Diagnostics;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc;
using WebApplication_ExpressionDemo.Models;
namespace WebApplication_ExpressionDemo.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly MyDbContext _myDbContext;
public HomeController(ILogger<HomeController> logger, MyDbContext myDbContext)
{
_logger = logger;
_myDbContext = myDbContext;
}
public IActionResult Index()
{
Expression<Func<Product, Boolean>> exp = p => p.Id > 0 && p.PName.Contains("vip");
CustomerVisitor visitor = new CustomerVisitor();
visitor.Visit(exp); // 解析表达式
Console.WriteLine(visitor.sql);
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
❡ 系统信息管理
❡ 管理服务
1 |
|
❡ WMI 查询
1 |
|
❡ 读取系统参数
❡ Environment 类
1 |
|
❡ SystemInformation 类
1 |
|
❡ 写入事件日志
1 |
|
❡ 进程与线程
❡ 进程操作
❡ Process 类
1 |
|
❡ 获取进程列表
1 |
|
❡ 重定向输入 / 输出流
1 |
|
❡ 多线程
❡ .net 中如何实现多线程
- 线程肯定也是要执行一段代码的。所以要产生一个线程,必须要先为这个线程写一段它即将要执行的一段代码(方法),相当于是找个人来帮忙做事。
- 线程启动时,通过委托来调用该方法(委托的好处),
- 线程启动时,调用传过来的委托,委托就会执行相应的方法,实现线程执行方法。
❡ 多线程开发
❡ 1. 单线程带来的问题
如果主线程需要执行某个占时很多的任务,如果此时再想对这个 UI 操作其他任务,就需要排队等待或者
出现无响应的情况。
1 |
|
在点击按钮的之后,UI 线程已经处于阻塞状态,界面无法拖动,只有当循环结束之后,我们才可以对界
面进行下一步操作。
❡ 2. 使用多线程解决问题
1 |
|
❡ 3. 前后台线程
Thread 对象在创建时,默认为前台线程,也就是 IsBackground = false 。前端线程的特点是:
- 只有当所有的前台线程都关闭,程序才会真正的退出。
- 即使主线程关了,前台线程也要将它所执行的任务执行完成才会退出。
1 |
|
❡ 4. 线程之间参数传递
1 |
|
❡ 线程安全
❡ 跨线程控件调用
❡ 问题
1 |
|
❡ 解决方案 1:
1 |
|
CheckForIllegalCrossThreadCalls =true; 的时候 .net 会对程序使用 UI 控件进行安全检查 ,避免跨线程
导致的死锁、状态不对的 bug 。而 CheckForIllegalCrossThreadCalls =false;.net 不会对程序使用 UI 控件
进行安全检查,全靠程序员自己避免以上 bug,实际上还是多个线程对界面控件操作 。
❡ 解决方案 2:
1 |
|
❡ 线程并发
❡ 抛出问题
1 |
|
❡ lock 关键字解决并发
作用: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为
互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这
是通过在代码块运行期间为给定对象获取互斥锁来实现的。在多线程中,每个线程都有自己的资源,但
是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个
函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。
缺点: 多线程中频繁使用 lock 会造成性能损耗。
1 |
|
❡ 注意事项及原理
2.1**** 注意事项
当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private static readonly object Lock =
new ();)或另一个不太可能被代码无关部分用作 lock 对象的实例。 避免对不同的共享资源使用相同的
lock 对象实例,因为这可能导致死锁或锁争用。
具体而言,避免将以下对象用作 lock 对象
- this(调用方可能将其用作 lock)。
- Type 实例(可以通过 typeof 运算符或反射获取)。
- 字符串实例,包括字符串文本,(这些可能是暂存的)。
- 尽可能缩短持有锁的时间,以减少锁争用。
- 在 lock 语句的正文中不能使用 await 运算符。
- 特别要注意,不要用值类型
2.2**** 原理 (以下内容比较浅显,太深究内容一篇文章写不完)
Q1**:** 大家会注意到,为什么要在 lock 的圆括号里放一个引用类型 object?为什么不可以放一个值类型如 int?
A1**:** 因为如果使用了值类型例如 int 作为 lock 锁定的对象,lock 圆括号中的入参是 object 类型当传入了值类型会对传入的对象类型进行转换,那么在 IL 层面会对值类型进行一次装箱(box)操作。那么这种情况下就不具备 lock 锁定需要用到专用对象的稳定性了。
IL_0002:ldloc.0
IL_0003:box [mscorlib]System.Int32
A2**:** 值类型 一般都在线程函数自己的栈里,每个线程局部栈是不一样的,互相之间不会有影响,所以不用锁定一个特例,引用类型值类型字段在堆里,但可以通过 lock 那个引用类型对象就可以实现了
❡ Monitor 监视器
首先 lock 和 Minitor 有什么区别呢?
其实 lock 在 IL 代码中会被翻译成 Monitor。也就是 Monitor.Enter (obj) 和 Monitor.Exit (obj).
微软很照护我们,给了我们语法糖 Lock, 对的,语言糖确实减少了我们不必要的劳动并且让代码更可观,但是如果我们要精细的控制,则必须使用原生类,这里要注意一个问题就是 “锁住什么” 的问题,一般情况下我们锁住的都是静态对象,我们知道静态对象属于类级别,当有很多线程共同访问的时候,那个静态对象对多个线程来说是一个,不像实例字段会被认为是多个。
1 |
|
等价于:
1 |
|
所以 lock 能做的,Monitor 肯定能做,Monitor 能做的,lock 不一定能做。那么 Monitor 额外的功能呢?
Monitor.TryEnter (obj,timespan)----timeout 之后,就不执行这段代码了。lock 可是一直会死等的。
还有 Monitor.Wait () 和 Monitor.Pulse ()。在 lock 代码里面如果调用了 Monitor.Wait (),会放弃对资源的所有权,让别的线程 lock 进来。然后别的线程代码里 Pulse 一下(让原线程进入到等待队列),然后在 Wait 一下释放资源,这样原线程的就可以继续执行了(代码还堵塞在 wait 那句话呢)。也就是说,必须两个或多个线程共同调用 Wait 和 Pulse,把资源的所有权抛来抛去,才不会死锁
❡ 生产者消费者模式(需要反复学习)
请用线程结合队列模拟生产者 - 消费者模式,需求描述如下:
- 生产者只在仓库未满时进行生产苹果手(最多生产 10 部)并放入队列,仓库满时生产者进程被阻塞
- 消费者只在仓库非空时进行消费,仓库为空时消费者进程被阻塞
- 当消费者发现仓库为空时通知生产者生产
- 当生产者发现仓库满时通知消费者
1 |
|
❡ Semaphore 信号量
❡ 案例
需求描述,某公共厕所共有 3 个蹲位 (3 个信号量),突然来了 10 个人,请使用 Semaphore 来模拟此场景。
1 |
|
❡ 线程等待,挂起,唤醒,终止
❡ 1. 线程等待 ****Join
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程
终止。
大白话就是,将执行权交给当前的实例线程,直到该线程的任务完成。
不等待
1 |
|
Join**** 等待
1 |
|
❡ 2. 挂起,唤醒,终止
1 |
|
❡ 线程池
1 |
|
❡ 异步编程
❡ Task 类
❡ 创建任务的三种方式:
1 |
|
❡ 任务等待的四种方式
1 |
|
❡ 执行延续任务
1 |
|
❡ 任务取消
Task 中有一个专门的类 CancellationTokenSource 来取消任务执行 。
1 |
|
CancellationTokenSource 的功能不仅仅是取消任务执行,我们可以使用 source.CancelAfter (5000) 实现 5 秒后自动取消任务,也可以通过 source.Token.Register (Action action) 注册取消任务触发的回调函数,即任务被取消时注册的 action 会被执行 .
1 |
|
❡ await 与 async
❡ 异步方法
异步方法必须用 async 修饰,方法名一般以 Async 结尾,这是一种命名规范
1 |
|
❡ 异步等待
如果某些方法的返回值,你需要立马得到结果才可以进行下一步操作时,可以使用 await 完成异步方法的完成。
❡ 等待结果
1 |
|
也可以这样拿结果
1 |
|
productTask.Result 其实也是等待结果的作用。
❡ Thread 与 Task**** 的区别(面试点)
Task 是基于 Thread 的,是比较高层级的封装(它是在线程池的基础之上), Task 最终还是需要 Thread 来执行
Task 默认使用后台线程执行, Thread 默认使用前台线程
Task 可以有返回值, Thread 没有返回值 , 虽然 Thread 可以通过 Start 方法参数来进行返回值处理,但十分不便。
Task 可以执行后续操作, Thread 不能执行后续操作
Task 可取消任务执行, Thread 不行
异常传播 , Thread 在父方法上获取不到异常,而 Task 可以。
❡ 并行任务
1 |
|
1 |
|
❡ Threadlocal
多个线程共享一个变量,但每个线程都希望具有独立的数据版本。
1 |
|
❡ AsyncLocal
1 |
|
❡ 通道
通道内部自动维护者一个线程安全的队列,数据的写入与读取可以在不同的线程上完成。
❡ Channel
❡ CreateUnbounded–没有容量限制
1 |
|
❡ CreateBounded–有容量限制,背压模式
1 |
|
❡ 网络编程(已经操作过一遍,后续还需要学习)
❡ Socket
❡ 客户端代码
1 |
|
❡ 服务端代码
1 |
|
❡ 常用工具类(还没完整学习,后续还需要学习)
❡ 1. 压缩与解压缩
- 将老师提供的 utils 文件夹 中的 ZipHelper.cs 类 复制到当前你的项目中
- ZipHelper 依赖于 ICSharpCode.SharpZipLib.dll ,通过 Nuget 可将其引用进来
- 创建一个 winform 项目,布局如下
❡ 选择单文件
1 |
|
❡ 压缩单文件
1 |
|
❡ 选择多文件
1 |
|
❡ 压缩多文件
1 |
|
❡ 解压文件
1 |
|
❡ 全部压缩
1 |
|
1 |
|
❡ 2. 图片处理
- 从老师提供的 utils 文件夹中将 ImageHelper 类拷贝至当前你的项目中
- 创建一个 winform 项目,布局如下
❡ 生成验证码
布局:
选择 tabcontrol,然后选择 pictureBox,PlaceholderText 进行输入内嵌文本
1 |
|
❡ 生成文字水印
1 |
|
❡ 生成图片水印
1 |
|
❡ 生成缩略图
定义好枚举,表示若干缩略图大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31/// <summary>
/// 图片大小
/// </summary>
public enum ImageSize
{
/// <summary>
/// 50×50
/// </summary>
[Description("50×50")]
Size_50 = 50,
/// <summary>
/// 100×100
/// </summary>
[Description("100×100")]
Size_100 = 100,
/// <summary>
/// 150×150
/// </summary>
[Description("150×150")]
Size_150 = 150,
/// <summary>
/// 220×220
/// </summary>
[Description("220×220")]
Size_220 = 220,
/// <summary>
/// 350×350
/// </summary>
[Description("350×350")]
Size_350 = 350
}从老师提供的 utils 文件夹将 EnumHelper 文件拷贝至当前你的项目中
生成缩略图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22private void txtGenerate_Click(object sender, EventArgs e)
{
//获取文件名称
var fileInfo = new FileInfo(txtImagePath.Text);
var fileName = fileInfo.Name.Split('.')[0];
var newPath = Path.Combine(fileInfo.DirectoryName, fileName);
if (Directory.Exists(newPath))
{
//如果这个要创建的文件夹存在 ,则删了,并且把子目录 也一并删除
Directory.Delete(newPath,true);
}
Directory.CreateDirectory(newPath);
//获取所有需要生成缩略图的尺寸
var sizes = EnumHelper.ToDescriptionDictionary<ImageSize>();
foreach (var size in sizes)
{
var thumbnailFilePath = Path.Combine(newPath, $"
{size.Key}_{size.Key}.png");
ImageHelper.CreateThumbnail(txtImagePath.Text,
thumbnailFilePath, size.Key, size.Key);
}
}
❡ 3. NPOI 操作
NPOI,顾名思义,就是 POI 的.NET 版本。 那 POI 又是什么呢? POI 是一套用 Java 写成的库,能够帮助开
发者在没有安装微软 Office 的情况下读写 Office 97-2007 的文件, 支持的文件格式包括 xls,xlsx,
doc,docx, ppt 等。
优缺点
读写速度快(有个国外的兄弟回复说,他原来用 ExcelPackage 生成用了 4-5 个小时,现在只需要 4-5 分钟)
稳定性好(相对于用 Office OIA 而言,毕竟那东西是基于 Automation 做的,在 Server 上跑个 Automation 的东西, 想想都觉得可怕),跑过了将近 1000 个测试用例(来自于 POI 的 testcase 目录)
API 简单易用,当然这得感谢 POI 的设计师们 第五,完美支持 Excel 2003 格式(据说 myxls 无法正确读取 xls 模板,但 NPOI 可以),以后也许是所有 Office 2003 格式
缺点
大文件操作占内存,对于数据量较大的文件导入操作可能不太支持。
功能实现
Nuget 引用包:NPOI,microsoft.entityframeworkcore,microsoft.entityframeworkcore.sqlserver
❡ Excel**** 导入
Html 代码
1 |
|
第一种实现方式:
注意点:id 得是 primary key 主键才行,不然会有很多问题。
1 |
|
第二种实现方式 (使用工具类):
从 utils 文件夹中拷贝 ExcelHelper 工具类至当前项目中,ExcelHelper 中对 IWorkBook 接口扩展了 Import 方法
1 |
|
❡ Excel 导出
ExcelHelper 中对 IWorkBook 接口扩展了 Export 方法
1 |
|
❡ 4. EasyExcel 操作
EasyExcel是一个简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
官网地址: https://easyexcel.opensource.alibaba.com/(阿里出品) , UP 主也推荐使用 EasyExcel。
Nuget 引用包: Rong.EasyExcel
up 主试过使用 EasyExcel 对 100 万条数据的 excel 进行操作过,并无性能瓶颈。
❡ ** 导入操作 **
准备实例类
1
2
3
4
5
6
7
8
9public class Product
{
[IgnoreColumn] // 因为Id是自增的,所以Excel表头中并无此列,必须忽略此字段
public int Id { get; set; }
[Display(Name = "名称")]
public string? PName { get; set; }
[Display(Name = "价格")]
public decimal? Price { get; set; }
}IgnoreColumn: 设置忽略某字段
Display: 与 Excel 表头形成映射关系,Name 属性值必须要与表头的列名保持一致
在 Program 类中将 EasyExcel 相关服务进行注册
1
builder.Services.AddNpoiExcel();
注入服务
1
2
3
4
5
6
7
8
9
10private IExcelImportManager _excelImportManager; // easyExcel 导入服务
private MyDbContext _myDbContext; // EF 服务
public ExcelController(
IExcelImportManager excelImportManager,
MyDbContext db
)
{
_excelImportManager = excelImportManager;
_myDbContext = db;
}完成导入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28[HttpPost]
public IActionResult Import(IFormFile myexcel)
{
using Stream stream = myexcel.OpenReadStream();
List<ExcelSheetDataOutput<Product>> data = new();
try
{
data = _excelImportManager.Import<Product>(stream,
p =>
{
p.CheckError();
// 检查错误
p.SheetIndex =
0;
});
// 获取有效数据
var allData = data.GetAllData().ToList();
_myDbContext.Product.AddRange(allData);
_myDbContext.SaveChanges();
return Content("导入完成");
}
catch (Exception e)
{
// 获取错误数据
var error = data.GetErrorMessage();
return Content(error);
}
}注意:需要把多余的 sheet 删掉
❡ 导出操作
注入导出服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28[HttpPost]
public IActionResult Import(IFormFile myexcel)
{
using Stream stream = myexcel.OpenReadStream();
List<ExcelSheetDataOutput<Product>> data = new();
try
{
data = _excelImportManager.Import<Product>(stream,
p =>
{
p.CheckError();
// 检查错误
p.SheetIndex =
0;
});
// 获取有效数据
var allData = data.GetAllData().ToList();
_myDbContext.Product.AddRange(allData);
_myDbContext.SaveChanges();
return Content("导入完成");
}
catch (Exception e)
{
// 获取错误数据
var error = data.GetErrorMessage();
return Content(error);
}
}注意:需要把多余的 sheet 删掉
导出操作
注入导出服务
1
2
3
4
5
6
7
8
9
10private readonly IExcelExportManager _excelExportManager;
private MyDbContext _myDbContext; // EF 服务
public ExcelController(
IExcelExportManager excelExportManager,
MyDbContext db
)
{
_excelExportManager = excelExportManager;
_myDbContext = db;
}实现导出
1
2
3
4
5
6
7[HttpGet]
public IActionResult Export()
{
var products = _myDbContext.Product.ToList();
return File(_excelExportManager.Export(products),
"application/ostet-stream", "产品列表.xls");
}
❡ 5. 雪花算法
❡ 1. 传统的主键策略
主键自增
传统的主键自增在分布式系统中极易出现主键重复情况,特别是在高并发的情况下。
GUID
GUID 的主要目的是产生完全唯一的数字。在理想情况下,任何计算机和计算机集群都不会生成两个相
同的 GUID。GUID 的总数也足够大,达到了 2128(3.4×1038)个,所以随机生成两个相同 GUID 的可
能性是非常小的,但并不为 0。所以,用于生成 GUID 的算法通常都加入了非随机的参数(如时间),以
保证这种重复的情况不会发生。
缺点:
1、存储空间大(16 byte),因此它将会占用更多的磁盘大小。 如果你建的索引越多, 影响越严重。
2、很难记忆。join 操作性能比整数要低。
3、没有内置的函数获取最新产生的 guid 主键。
4、GUID 做主键将会添加到表上的其他索引中,因此会降低性能,影响插入速度。
5、GUID 之间比较大小相对数字慢不少, 影响查询速度
❡ 2. SnowFlake 算法 - 分布式 ID
SnowFlake算法生成id的结果是一个64bit大小的整数 ,它是一个分布式ID,分布式ID 具有以下特点:
优点:
毫秒数在高位,自增序列在低位,整个 ID 都是趋势递增的。
作为 DB 表的主键,索引效率高。
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成 ID 的性能也是非常高的。
高性能高可用:生成时不依赖于数据库,完全在内存中生成。
容量大,每秒中能生成数百万的自增 ID。
可以根据自身业务特性分配 bit 位,非常灵活。
缺点:
- 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
- 不是严格全局递增的 (有点像莫须有的罪名,也不算是缺点)。
❡ 3. 原理结构图
1bit,不用,因为二进制中最高位是符号位,1 表示负数,0 表示正数。生成的 id 一般都是用整数,所以最高位固定为 0。
41bit 时间戳,毫秒级。可以表示的数值范围是 (2^41-1),转换成单位年则是 69 年。
10bit 工作机器 ID,用来表示工作机器的 ID,包括 5 位 datacenterId 和 5 位 workerId。也就是最多可以部署 1024 台雪花机器。
12bit 序列号,用来记录同毫秒内产生的不同 id,12 位可以表示的最大整数为 4095,来表示同一机器同一时间截(毫秒) 内产生的 4095 个 ID 序号。 如果超过这个数字,则会等下一毫秒再生成
服务一定需要开启才有用,而且端口号得用开启后的端口 6379 才行不然连接不上
redis-server.exe redis.windows.conf 执行该命令
❡ 4. .Net 实现
在提供的 utils 文件夹中拷贝 Snowflake 文件夹至当前项目中,里面包含了 IdWorker(雪花语法的具体实现)
1 |
|
❡ 5. 雪花算法常见问题
其实我们之前有提到工作机器 Id 的作用,就是用于解决分布式 Id 重复的问题,这个 workerId 是通过构造
方法传入的,如果我们用 10 位来存储这个值,那就是最多支持 1024 个节点
WorkerId 重复问题
如果 workerId 相同,意味着在同一毫秒内,生成的雪花 Id 是有非常大的机率重复。虽然雪花 Id 共 64 位,但是同一毫秒,同一个 workerId ,意味着前 42+10 = 52 位 都是相同的数字,仅靠 剩下的 12 位序列号是很难保证分布式系统中雪花 ID 不重复。所以不同的服务器,它所用的 WorkerId 必须不同,这样才能尽可能的避免并发情况下 Id 重复问题。
解决方案
在 redis 中存储一个当前 workerId 的最大值
每次生成 workerId 时,从 redis 中获取到当前 workerId 最大值,并 + 1 作为当前 workerId,并存入 redis
如果 workerId 为 1023,自增为 1024,则重置 0,作为当前 workerId,并存入 redis
IdWorker 这个类必须设置为单例模式
代码实现
- 项目中添加 Nuget 包,搜索 Microsoft.Extensions.Caching.StackExchangeRedis(版本一定要比 redis 的版本低才行)
- 优化雪花算法工具包封装类
1 |
|
❡ Linq 高级查询
❡ 延迟执行与强制立即执行
LinQ 查询是延迟执行,运行到 foreach 语句处会跳回 select 语句处。
1 |
|
❡ Linq**** 实现的方式
1 |
|
❡ 联合查询
1 |
|
❡ 嵌套查询
1 |
|
❡ Linq - 标准语法
1 |
|
1 |
|
❡ 技术篇
❡ 数据库
❡ 关系数据库
❡ MySQL
windows 身份验证
SqlServer 身份验证
127.0.0.1、localhost、查询 ip 地址是本地
数据库操作:1、使用 SSMS 方式,2、T-SQL 方式
❡ 数据库的操作
1 |
|
❡ 创建表操作
1 |
|
❡ 表约束
1 |
|
❡ 表数据操作
1 |
|
❡ 简单查询
1 |
|
❡ 条件查询、分组查询
1 |
|
❡ 嵌套查询操作
1 |
|
❡ 连接查询
1 |
|
❡ Oracle
❡ SqlServer
❡ SQLite
❡ DB2
❡ MariaDB
❡ PostgreSQL
❡ 非关系数据库
❡ MongoDb
❡ Redis
❡ NoSql
❡ LiteDB
❡ Apache Cassandra
❡ RavenDB
❡ CouchDB
❡ 搜索引擎
❡ ElasticSearch
❡ Solr
❡ Sphinx
❡ 云数据库
❡ Azure CosmosDB
❡ Amazon DynamoDB
❡ 桌面篇
❡ WPF
❡ WinForm
❡ Linux
❡ RabbitMq
❡ Web 开发
❡ Asp.Net Core
❡ MVC 学习
❡ 快速入门学习
❡ Mvc**:约定大于配置 **
- 控制器类加 Controller 后缀,而且都放在 Web 项目下的 Controllers 文件夹中,控制器类继承 Controller 基类。
- 视图文件必须放在名称为 Views/Pages 的文件夹下的名称为控制器名称的文件夹中。
- _ViewStart.cshtml 执行任何 Action (控制器中以 IActionResult 为返回类型的方法叫 Action 方法) 之前,都会先执行它.
- 以下划线命名开头的视图一般作为布局 / ViewCompenent 视图,放在 shared 文件夹下面
- _ViewImport.cshtm 为全局视图文件公共命名空间的引用
❡ .net5 环境测试
1 |
|
1 |
|
1 |
|
❡ Home/Hello.cshtml
1 |
|
❡ Student/StudentList.cshtml
1 |
|
❡ HomeController.cs
1 |
|
❡ StudentController.cs
1 |
|
❡ _ViewStart.cshtml
1 |
|
❡ _ViewImports.cshtml
1 |
|
❡ Shared/_Layout.cshtml
这是共享的文件,类似于导航栏,可用可不用,区分于_ViewStart.cshtml 的公共文件
1 |
|
❡ Models/StudentViewModel.cs
1 |
|
❡ MVC 项目结构
- Dependencies:项目所依赖的组件
- launchSettings.json : 项目发布设置文件
1 |
|
- Controllers: 存放所有的控制器
- Models: 存放所有的 ViewModel 文件
- Views: 存放所有的视图文件
- Views/Shared : 存放公共的视图文件
- Views/Shared/_Layout.cshtml : 公共布局文件
- Views/Shared/Error.cshtml: 错误提示视图
- Views/_ViewImports.cshtml: 公共导入命名空间,引用公共的标签助手
1 |
|
- Views/_ViewStart.cshtml: 视图起始文件,所有视图在加载时,都会先加载此视图文件
- wwwroot: 存放所有的静态资源文件(css/js/html)
- wwwroot/favicon.ico : 应用程序的图标
- appsetting.json: 当前应用程序的配置文件
- appsetting.Development.json: 当前环境的配置文件,如果在此文件中未找到想要的配置,则会去 appsetting.json 文件中去寻找。
- Program:程序的主入口,用于初始化系统的相关配置,注册服务,配置中间件与注册管道。
❡ 控制器动作
1 |
|
❡ Action**** 方法与普通方法的区别
Action 方法是由 Mvc 框架管理,Mvc 框架可以对 Action 方法进行处理与渲染(例如渲染视图,拦截请求等等),而普通则不受 mvc 控制。
举个例子,ContentResult 通常的作用也是直接返回一个字符串,当我们执行 Content (「hello, 任我行码农场」) 时,我们只是告诉 Mvc 框架,我们需要返回 “hello, 任我行码农场” ,而并非立即返回,Mvc 框架在此之后可能还会做很多的处理,或许在中间的某个环节,有可能请求被拦截,导致我们可能得到不同的结果(日后要讲的 AOPA 思想)。而 return 「hello, 任我行码农场」 则是立即返回。
❡ Razor**** 视图
❡ 注释
ctrl+? 是所有注释,ctrl+/ 是每行都有注释
viewmodel 是跟视图模型有关的数据,其他三个是无关的数据。
既可以使用四种传值方式或者建立 Model 的方式。
1 |
|
1 |
|
❡ Student/IndexHello.cshtml
1 |
|
❡ Student/Add.cshtml
1 |
|
❡ Controllers/StudentController.cs
1 |
|
❡ 总结
1 |
|
❡ 四种传值方式:
❡ 视图传值
❡ 1、TempData [“xxx”] = xxx; 需要强制转换
1 |
|
1 |
|
TempData 保存在 Session 中,Controller 每次执行请求的时候,会从 Session 中先获取 TempData,而后清除 Session,获取完 TempData 数据,虽然保存在内部字典对象中,但是其集合中的每个条目访问一次后就从字典表中删 除。具体代码层面,TempData 获取过程是通过 SessionStateTempDataProvider.LoadTempData 方法从 ControllerContext 的 Session 中读取数据,而后清除 Session,故 TempData 只能跨 Controller 传递一次。
1 |
|
❡ ViewBag 与 ViewData
ViewData:
ViewData 只在当前 Action 中有效,生命周期和 View 相同;
❡ 2、ViewData [「xxx」] = xxx; 需要强制转换
1 |
|
1 |
|
1 |
|
ViewBag
ViewBag 其实本质就是 ViewData,只是多了层 Dynamic 控制。所以,使用何种方式完全取决于你的个人爱好。
1 |
|
1 |
|
❡ 3、ViewBag.xxx = xxx; 不需要强制转换
1 |
|
两者区别如下:
总结
1、ViewData 和 TempData 是字典类型,赋值方式用字典方式,ViewData [「myName」]
2、ViewBag 是动态类型,使用时直接添加属性赋值即可 ViewBag.myName
3、ViewBag 和 ViewData 只在当前 Action 中有效,等同于 View
4、TempData 可以通过转向继续使用 (Server.Tranfer ()),因为它的值保存在 Session 中。但 TempData 只能经过一次传递,之后会被系统自动清除 (Framework)
5、ViewData 和 ViewBag 中的值可以互相访问,因为 ViewBag 的实现中包含了 ViewData
❡ 4、ViewModel
1 |
|
❡ Http
❡ 前置知识
客户端与服务端之间的通讯是否也需要某种协议?
答:http 协议. http 协议是一种未进行加密处理,由服务器传输超文本到本地浏览器传输协议。
特点:
- 基于 TCP/IP 的高级协议 (Socket)
- 默认端口号:80
- 基于请求 / 响应模型的: 一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据
- HTTP 协议在应用层
- 最初的目的是为了提供一种发布和接收 HTML 页面的方法
- 规定了客户端和服务器之间通信格式
❡ Http 的通信流程
- 建立 TCP 连接
- 客户端向服务端发送命令
- 客户端发送请求头信息
- 服务器应答
- 服务器发送应答头信息
- 服务器向客户端发送数据(静态资源,html/css/js)
- 服务器关闭 TCP 连接
一般情况下,一旦 Web 服务器向浏览器发送了请求数据,它就要关闭 TCP 连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive
TCP 连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
❡ URL 与 URI**** 的区别
❡ 什么是 ****URI
URI 统一资源标识符(Uniform Resource Identifiers, URI),用来唯一识别一个资源,可以把它理解为你的身份证号。
作用:Web 上可用的资源如 HTML 文档,图像,视频等都是以 URI 来定位的。
❡ 什么是 ****URL
URL 统一资源定位符( Uniform Resource Locator ),可以把它理解为你身份证上地址。
是互联网上用来标识某一处资源的地址。
作用:
可以用来标识一个资源,而且还指明了如果定位这个资源
URL 是 internel 上用来描述信息资源的字符串,主要用在各种 www 程序上。
区别
URI 是一种抽象的,高层次概念定义统一资源标识
每个 URL 都是一个 URI,但每一个 URI 并不一定是 URL,因为 URI 还包括另外一个子类 URN(统一资源命名),它命名资源但不负责定位资源(姓名 + 你的地址 = 你的身份证号)
URL 就是通过定位的方式来实现 URI 的。
❡ 消息数据格式
❡ 请求消息格式
- 请求行
1 |
|
- 请求方式:
HTTP 协议有 8 中请求方式:
① GET:请求获取 Request-URI 所标识的资源。
② POST:在 Request-URI 所标识的资源后附加新的数据。
③ HEAD:请求获取由 Request-URI 所标识的资源的响应消息报头。
④ PUT:请求服务器存储一个资源,并用 Request-URI 作为其标识。
⑤ DELETE:请求服务器删除 Request-URI 所标识的资源。
⑥ TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断。
⑦ CONNECT:HTTP 1.1 协议中预留给能够将连接改为管道方式的代理服务器。
⑧ OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求。
常用的有 2 种
GET:
- 请求参数在请求行中,在 url 后。
- 请求的 url 长度有限制的
- 不太安全
- 可被收藏到书签,也可被缓存
POST:
- 请求参数在请求体中
- 请求的 url 长度没有限制的
- 相对安全
- 请求头:客户端浏览器告诉服务器一些信息
1 |
|
常见的请求头:
- User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
作用: 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- Referer:http://localhost/login.html ,告诉服务器,我 (当前请求) 从哪里来。
作用:
- 防盗链:
- 统计工作:
- 请求空行 :就是用于分割 POST 请求的请求头,和请求体的。
- 请求体 (正文): 封装 POST 请求消息的请求参数的
字符串格式:
1 |
|
❡ 响应数据格式
响应消息:服务器端发送给客户端的数据
响应行
1 |
|
响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态,状态码都是 3 位数字。
分类
1xx:服务器接收客户端消息,但没有接受完成,等待一段时间后,发送 1xx 状态码
2xx:成功。代表:200
3xx:重定向。代表:302 (重定向),304 (访问缓存)
4xx:客户端错误。代表:404(请求路径没有对应的资源)405:请求方式没有对应的方法, 401 未授权,403?
5xx:服务器端错误。代表:500 (服务器内部出现异常),503:网关出现问题
响应头
格式:
1 |
|
常见的响应头:
Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
Content-disposition:服务器告诉客户端以什么格式打开响应体数据值
attachment;filename=xxx:以附件形式打开响应体。文件下载
响应体
服务器返回的数据
响应字符串格式:
1 |
|
❡ HTTP 各版本简介
HTTP 1.0 : 规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个 TCP 连接,服务器完成请求处理后立即断
开 TCP 连接,服务器不跟踪每个客户也不记录过去的请求 。连接无法复用
HTTP1.1 :
复用连接(keep-alive)
缓存处理
身份认证, 状态管理
❡ HTTP 1.1**** 状态代码及其含义
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx :指示信息–表示请求已接收,继续处理
2xx :成功–表示请求已被成功接收、理解、接受
3xx :重定向–要完成请求必须进行更进一步的操作
4xx :客户端错误–请求有语法错误或请求无法实现
5xx :服务器端错误–服务器未能实现合法的请求
HTTP2.0 :
❡ 多路复用 (Multiplexing)
即连接共享,即每一个 request 都是是用作连接共享机制的。一个 request 对应一个 id,这样一个连接上可以有多个 request,每个连接
的 request 可以随机的混杂在一起,接收方可以根据 request 的 id 将 request 再归属到各自不同的服务端请求里面。多路复用原理和 keepalive 区别如下图:
二进制分帧
HTTP1.x 的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认 0 和 1 的组合。基于这种考虑 HTTP2.0 的协议解析决定采用二进制格式,实现方便且健壮。
首部压缩(Header Compression)
如上文中所言,对前面提到过 HTTP1.x 的 header 带有大量信息,而且每次都要重复发送,HTTP2.0 使用 encoder 来减少需要传输的 header 大小,通讯双方各自 cache 一份 headerfields 表,既避免了重复 header 的传输,又减小了需要传输的大小。
服务端推送(Server Push)
服务端推送是一种在客户端请求之前发送数据的机制。在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应。Server Push 让 HTTP1.x 时代使用内嵌资源的优化手段变得没有意义;如果一个请求是由你的主页发起的,服务器很可能会响应主页内容、logo 以及样式表,因为它知道客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源,不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
❡ HTTP 3.0
HTTP3.0,也称作 HTTP over QUIC。HTTP3.0 的核心是 QUIC (读音 quick) 协议,由 Google 在 2015 年提出的 SPDY v3 演化而来的新协议,传统的 HTTP 协议是基于传输层 TCP 的协议,而 QUIC 是基于传输层 UDP 上的协议,可以定义成:HTTP3.0 基于 UDP 的安全可靠的 HTTP2.0 协议。
QUIC 协议针对基于 TCP 和 TLS 的 HTTP2.0 协议解决了下面的问题。
1.1 减少了 TCP 三次握手及 TLS 握手时间
不管是 HTTP1.0/1.1 还是 HTTPS,HTTP2.0,都使用了 TCP 进行传输。HTTPS 和 HTTP2 还需要使用 TLS 协议来进行安全传输。这就出现了两个握手延迟,而基于 UDP 协议的 QUIC,因为 UDP 本身没有连接的概念,连接建立时只需要一次交互,半个握手的时间。区别如下图:
1.2 多路复用丢包的线头阻塞问题
QUIC 保留了 HTTP2.0 多路复用的特性,在之前的多路复用过程中,同一个 TCP 连接上有多个 stream,假如其中一个 stream 丢包,在重传前后的 stream 都会受到影响,而 QUIC 中一个连接上的多个 stream 之间没有依赖。所以当发生丢包时,只会影响当前的 stream,也就避免了线头阻塞问题。
1.3 优化重传策略
以往的 TCP 丢包重传策略是:在发送端为每一个封包标记一个编号 (sequence number),接收端在收到封包时,就会回传一个带有对应编号的 ACK 封包给发送端,告知发送端封包已经确实收到。当发送端在超过一定时间之后还没有收到回传的 ACK,就会认为封包已经丢失,启动重新传送的机制,复用与原来相同的编号重新发送一次封包,确保在接收端这边没有任何封包漏接。这样的机制就会带来一些问题,假设发送端总共对同一个封包发送了两次 (初始+重传),使用的都是同一个 sequence number: 编号 N。之后发送端在拿到编号 N 封包的回传 ACK 时,将无法判断这个带有编号 N 的 ACK,是接收端在收到初始封包后回传的 ACK。这就会加大后续的重传计算的耗时。QUIC 为了避免这个问题,发送端在传送封包时,初始与重传的每一个封包都改用一个新的编号,unique packet number,每一个编号都唯一而且严格递增,这样每次在收到 ACK 时,就可以依据编号明确的判断这个 ACK 是来自初始封包或者是重传封包。
1.4 流量控制
通过流量控制可以限制客户端传输资料量的大小,有了流量控制后,接收端就可以只保留相对应大小的接收 buffer , 优化记忆体被占用的空间。但是如果存在一个流量极慢的 stream ,光一个 stream 就有可能估用掉接收端所有的资源。QUIC 为了避免这个潜在的 HOLBlocking,采用了连线层 (connection flow control) 和 Stream 层的 (streamflow control) 流量控制,限制单一 Stream 可以占用的最大 buffer size。
1.5 连接迁移
TCP 连接基于四元组(源 IP、源端口、目的 IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间。QUIC 的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。QUIC 连接不以四元组作为标识,而是使用一个 64 位的随机数,这个随机数被称为 Connection lD,对应每个 stream,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。
❡ 什么是 ****HTTPS
HTTP 协议传输的数据都是未加密的。为了保证这些隐私数据能加密传输,于是网景公司设计了 SSL (Secure Sockets Layer)协议用于对 HTTP 协议传输的数据进行加密,从而就诞生了 HTTPS 。现在的 HTTPS 都是用的 TLS 协议,但是由于 SSL 出现的时间比较早,并且依旧被现在浏览器所支持,因此 SSL 依然是 HTTPS 的代名词。
HTTPS 默认端口号是 443.(http 协议默认端口号是 80)
HTTPS 与 HTTP**** 的一些区别
- HTTPS 协议需要到 CA 申请证书,一般免费证书很少,需要交费。
- HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 TLS 加密传输协议。
- HTTP 和 HTTPS 使用的是完全不同的连接方式,用的默认端口也不一样,前者是 80,后者是 443.
- HTTPS 的连接很简单,HTTPS 协议是由 TLS+HTTP 协议构建的 可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
❡ HttpContext**** 上下文
❡ 1、请求案例
首先需要先跳转到 requesttest/search 下的页面进行搜索提交,然后断点处才会出现结果,否则为 null。
1 |
|
1 |
|
❡ 2、HttpContext
1 |
|
❡ 错误写法 1
显示不出结果为空
❡ homeController/index
1 |
|
❡ Service/IStudentService.cs
1 |
|
❡ Service/StudentService.cs
1 |
|
❡ 错误写法 2
可以显示出输入的参数,但是如果多个方法的话需要传参多次,不方便也很麻烦。
❡ homeController/index
1 |
|
❡ Service/IStudentService.cs
1 |
|
❡ Service/StudentService.cs
1 |
|
❡ 正确写法
❡ homeController/index
1 |
|
❡ Service/IStudentService.cs
1 |
|
❡ Service/StudentService.cs
1 |
|
❡ Startup.cs
1 |
|
❡ 总结
1 |
|
1 |
|
❡ 3、HttpRequest
❡ RequestController/add
1 |
|
❡ Request/Add.cshtml
1 |
|
❡ 4、HttpResponse
1 |
|
❡ 5、Http 响应体
1 |
|
❡ 中间件
概念:在 ASP.NET Core 中,中间件 (Middleware) 是一个可以处理 HTTP 请求或响应的软件管道。 ASP.NET Core 中给中间件组件的定位是具有非常特定的用途。例如,我们可能有需要一个中间件组件验证用户,另一个中间件来处理错误,另一个中间件来提供静态文件,如 JavaScript 文件,CSS 文件,图片等等。
中间件就是用于组成应用程序管道来处理请求和响应的组件 。
中间件可以认为有两个基本的职责:
- 选择是否将请求传递给管道中的下一个中间件。
- 可以在管道中的下一个中间件前后执行一些工作。
我们使用这些中间件组件在 ASP.NET Core 中设置请求处理管道,而正是这管道决定了如何处理请求。 而请求管道是由 Startup.cs 文件中的 Configure() 方法进行配置,它也是应用程序启动的一个重要部分。
误区:面试官说的中间件是指第三方组件,而这边学习的是中间件 (Middleware) 是一个可以处理 HTTP 请求或响应的软件管道。
❡ .net5 以上
1 |
|
❡ .net5
1 |
|
❡ 管道
.Net Core**** 管道(pipeline)是什么?
简单来说,就是从发起请求到返回结果的一个过程,在.Net Core 中这里面的处理是由中间件(middleware)来完成。 管道机制解释 用户在发起请求后,系统会自动生成一个请求管道(request pipeline),在这个请求管道中,可以通过 run、map 和 use 方法来配置请求委托 (RequestDelegate),而在单独的请求委托中定义的可重用的类和并行的匿名方法即为中间件,也叫做中间件组件。当发起请求后,系统会创建一个请求管道,在这个管道中,每一个中间件都会按顺序处理(可能会执行,也可能不会被执行,取决于具体的业务逻辑),等最后一个中间件处理完后,又会按照相反的方向返回最终的处理结果。
例如,如果您有一个日志记录中间件,它可能只是记录请求的时间,它处理完毕后将请求传递给下一个中间件以进行进一步处理。
1 |
|
❡ 中间件顺序
下图显示了 ASP.NET Core MVC 和 Razor Pages 应用的完整请求处理管道。 你可以在典型应用中了解现有中间件的顺序,以及在哪里添加自定义中间件。 你可以完全控制如何重新排列现有中间件,或根据场景需要注入新的自定义中间件。
❡ 短路方式
中间件组件可以处理请求,并决定不调用管道中的下一个中间件,从而使管道短路,官方微软给了一个英文的名字叫 “terminalmiddleware” , 翻译为 “终端中间件”。短路通常是被允许的,因为它可以避免一些不必要的工作。 例如,如果请求的是像图像或 css 文件这样的静态文件,则 StaticFiles 中间件可以处理和服务该请求并使管道中的其余部分短路。这个意思就是说,在我们的示例中,如果请求是针对静态文件,则 Staticile 中间件不会调用 MVC 中间件,避免一些无谓的操作
1、注释方式
1 |
|
2、app.run 方式
1 |
|
❡ app.Use 与 app.Run 的区别
它俩都可以添加一个中间件至请求管道中。
- Use 有权决定是否执行下一个中间件,如果不执行,则出现短路情况
- Run 是直接短路,不会执行后面的中间件。
❡ 终端节点
1 |
|
❡ 路由中间件
❡ 路由模板:
- 在启动时 Program.cs 或在属性中定义。
- 描述 URL 路径如何与操作相匹配。
- 用于生成链接的 URL。 生成的链接通常在响应中返回。
操作既支持传统路由,也支持属性路由。 通过在控制器或操作上放置路由可实现属性路由。 有关详细信息,请参阅混合路由。
1 |
|
❡ 设置传统路由
1 |
|
路由模板 「{controller=Home}/{action=Index}/{id?}」 :
- 匹配 URL 路径,例如 /Products/Details/5
- 通过标记路径来提取路由值 {controller = Products, action = Details, id = 5} 。 如果应用有一个名为 ProductsController 的控制器和一个 Details 操作,则提取路由值会导致匹配:
1 |
|
- /Products/Details/5 模型绑定 id = 5 的值,以将 id 参数设置为 5 。 有关更多详细信息,请参阅模型绑定。
- {controller=Home} 将 Home 定义为默认 controller 。
- {action=Index} 将 Index 定义为默认 action 。
- {id?} 中的?字符将 id 定义为可选。
- 默认路由参数和可选路由参数不必包含在 URL 路径中进行匹配。 有关路由模板语法的详细说明,请参阅路由模板参考。
- 匹配 URL 路径 / 。
- 生成路由值 {controller = Home, action = Index} 。
controller 和 action 的值使用默认值。 id 不会生成值,因为 URL 路径中没有相应的段。 / 仅在存在 HomeController 和 Index 操作时匹配:
1 |
|
使用前面的控制器定义和路由模板,为以下 URL 路径运行 HomeController.Index 操作:
- /Home/Index/17
- /Home/Index
- /Home
- /
URL 路径 / 使用路由模板默认 Home 控制器和 Index 操作。 URL 路径 /Home 使用路由模板默认 Index 操作。
简便方法 MapDefaultControllerRoute:
1 |
|
替代:
1 |
|
1 |
|
❡ 属性路由
1 |
|
❡ 路由约束
1 |
|
❡ 疑惑解答:
1. 当访问一个 ****Web ** 应用地址时,**Asp.Net Core 是怎么执行到 Controller 的 Action 的呢?
答:程序启动的时候会把所有的 Controller 中的 Action 映射存储到 routeOptions 的集合中,Action 映射成 Endpoint 终结者 的 RequestDelegate 委托属性,最后通过 UseEndPoints 添加 EndpointMiddleware 中间件进行执行,同时这个中间件中的 Endpoint 终结者路由已经是通过 Rouing 匹配后的路由。
2. EndPoint 跟普通路由又存在着什么样的关系?
答: Ednpoint 终结者路由是普通路由 map 转换后的委托路由,里面包含了路由方法的所有元素信息 EndpointMetadataCollection 和 RequestDelegate 委托。
3. UseRouing() 、 UseAuthorization() 、 UseEndpoints() 这三个中间件的关系是什么呢?
答: UseRouing 中间件主要是路由匹配,找到匹配的终结者路由 Endpoint ; UseEndpoints 中间件主要针对 UseRouing 中间件匹配到的路由进行 委托方法的执行等操作。 UseAuthorization 中间件主要针对 UseRouing 中间件中匹配到的路由进行拦截 做授权验证操作等,通过则执行下一个中间件 UseEndpoints () , 具体的关系可以看下面的流程图:
上面流程图中省略了一些部分,主要是把 UseRouing 、UseAuthorization 、UseEndpoint 这三个中间件的关系突显出来。
❡ 异常中间件
1 |
|
1 |
|
这个页面的出现是选择了选择调试才出现的.
1 |
|
❡ launchSettings.json
1 |
|
❡ Program.cs
1 |
|
❡ 静态资源中间件
1 |
|
❡ Session
这里的问题是没有加入 httpcontext… 上下文服务所导致的
1 |
|
❡ Session 相关属性与配置
1 |
|
❡ Session 存储序列化对象
1 |
|
❡ Session 扩展方法
1 |
|
❡ 扩展方法需要放在 Utils 文件下
1 |
|
❡ 对象序列化 - ProtoBuf-Net
nuget 安装 protobuf-net
1 |
|
❡ 视图与模型
❡ 背景
有一个基于数据库的原始数据实体,但是后面再创建一个基于这个数据实体进行按需所给。
❡ 使用视图模型的好处:
面对那些业务场景不需要的字段我们不应该返回给前端,
方便此业务场景的维护,就算将来当前业务场景发生变化,也不至于影响到其他同学的调用。
字段太多,对于其他调用者来说不太友好,别人不知道这个字段是干嘛用的,特别是现在流行微服务开发,当我们给别人提供接口时,切记要记得 “按需所给” ,否则别人可能会为了你这些 “没用的字段” 而 去大费周章的去东挪西凑。
更符合当前的 DDD 开发模式。
❡ Ado.net 实现数据的显示与添加功能
❡ SQLServer 创建数据库 MvcUnit4
1 |
|
❡ 创建 DataModel 文件夹
❡ BaseBo.cs
1 |
|
❡ BaseEntity.cs
1 |
|
❡ Product.cs
1 |
|
❡ UserInfo.cs
1 |
|
❡ 创建 Model 文件夹
❡ ProductCreateBo.cs
1 |
|
❡ ProductViewModel.cs
1 |
|
❡ 修改 appsettings.json
1 |
|
❡ 创建 Services 服务
❡ IProductService.cs 接口
1 |
|
❡ ProductService.cs
1 |
|
❡ 修改 views 文件夹、添加 Product 文件夹
❡ Add.cshtml
1 |
|
❡ Search.cshtml
1 |
|
❡ 添加 Utils 文件夹
❡ DbHelper.cs
需要导入对应的包 system.data.sqlclient (得是 4.8.3) 如果是 4.9 的化就会被弃用。
1 |
|
❡ 添加 SnowFalke 文件夹
❡ IdWorker.cs
1 |
|
❡ SnowflakeUtil.cs
1 |
|
❡ 创建 Profiles 文件夹
❡ ProductProfile.cs
1 |
|
❡ 添加 ProductController 文件
❡ ProductController.cs
1 |
|
❡ 修改 Program.cs
❡ Program.cs
1 |
|
❡ AutoMap 组件 **-** 自动映射 (下划线的还有点问题后续解决问题)
❡ 添加包
AutoMapper.Extensions.Microsoft.DependencyInjection (11.0 版本还在,12 的版本已经弃用)
❡ 创建 Profile 文件夹
❡ CustomerProfile.cs
1 |
|
❡ 修改 Program.cs
1 |
|
❡ ProductService.cs
1 |
|
❡ 创建 AutomapModels 文件夹
❡ 命名方式 camelCase/PascalCase
作用:驼峰命名与 Pascal 命名的兼容。
以下全局配置会映射 property_name 到 PropertyName
❡ Product_1_2.cs
1 |
|
❡ 映射时匹配前缀或后缀
❡ CustomerProfile
1 |
|
❡ program.cs
1 |
|
❡ Product_1_4.cs
1 |
|
❡ TestAutoMapController
1 |
|
❡ 测试集合映射
1 |
|
❡ 手动控制某些成员的映射
❡ testautomapcontroller
1 |
|
❡ userinfo2_1.cs
1 |
|
❡ userprofile.cs
1 |
|
❡ 嵌套(Nested)类和继承类映射
❡ 嵌套类
❡ Product2_2.cs
1 |
|
❡ CustomerProfile.cs
1 |
|
❡ TestAutoMapController
1 |
|
❡ 继承类
❡ ParentSource.cs
1 |
|
❡ TestAutoMapController
1 |
|
❡ UserProfile.cs
1 |
|
❡ Html 辅助标签
作用:可以用于模型校验,虽然前端有一套,但是后端也有一套的话会更好。
❡ StudentController.cs
1 |
|
❡ Add.cshtml
1 |
|
❡ Search.cshtml
1 |
|
❡ 标签补充
❡ Add.cshtml
1 |
|
❡ Shared/_StudentView.cshtml
1 |
|
❡ Models/StudentBo.cs
1 |
|
❡ Dapper 实现增删改查
❡ SQL Server 创建表并添加数据
1 |
|
❡ 添加包
Dapper
❡ Profile 文件夹
❡ StudentProfile.cs
1 |
|
❡ DataModel 文件夹
❡ StudentInfo.cs
1 |
|
❡ StudentController.cs
1 |
|
❡ Models 文件夹
一个表维护三个类,一个跟数据库打交道的类 studentinfo.cs,一个添加修改的类 studentbo.cs,一个数据显示的类 studentviewmodel.cs
❡ StudentBo.cs
这不是跟数据库打交道的类,也就是充当传输的作用
1 |
|
❡ StudentViewModel.cs
好处是这边发生更改不影响 bo 也不影响实体类也就是 studentinfo,这是用于给用户展示的
1 |
|
❡ Services 文件夹
❡ IStudentService.cs
1 |
|
❡ StudentService.cs
1 |
|
❡ Program.cs
1 |
|
❡ Views 文件夹
❡ /Student/Search.cshtml
1 |
|
❡ /Student/Edit.cshtml
1 |
|
❡ Mvc Core 分页查询
❡ 添加包
PagedList.Mvc–framwork
X.PagedList.Mvc.Core–.net6.0
❡ SQL Server 页面
❡ /Student/Search.cshtml
1 |
|
❡ IStudentService.cs
1 |
|
❡ StudentService.cs
1 |
|
❡ StudentController.cs
1 |
|
❡ 强类型辅助方法及分布视图
❡ /Student/Add.cshtml
1 |
|
❡ /Shared/_StudentView.cshtml
1 |
|
❡ /Student/Search.cshtml
1 |
|
❡ 使用 HTML 辅助方法载入分部视图
使用 Html.Partial 载入分布视图
@Html.Partial(「Page」)
@Html.Partial(「Page」,Model)
@Html.Partial(「Page」,ViewData[「Model」])
@Html.Partial (「Page」,Model,ViewData [「Model」])// 两个参数使用
RenderPartial 辅助方法与 Partial 非常相似,但 RenderPartial 不是返回字符串,而是直接写入响应输出流。出于这个原因,必须把 RenderPartial 放入代码块中,而不能放在代码表达式中,为了说明这一点,下面两行代码向输出流写入相同的内容:
@
@Html.Partial(「Page」)
一般情况下,因为 Partial 相对于 RenderPartial 来说更方便,所以应该选择 Partial。然而,RenderPartial 拥有较好的性能,因为它是直接写入响应流的,但这种性能优势需要大量的使用才能看出来。
使用 Html.Action 辅助方法,从控制器载入分布视图
Action 和 RenderAction 类似于 Partial 和 RenderPartial 辅助方法。Partial 辅助方法通常在单独的文件中应用视图标记来帮助视图渲染视图模型的一部分。另一方面,Action 执行单独的控制器操作,并显示结果。Action 提供了更多的灵活性和重用性,因为控制器操作可以建立不同的模型,可以利用单独的控制器上下文。
1 |
|
利用 Controller 类型中的 PartialView 辅助方法来载入分布视图,而这种载入方式与用 View 辅助方法唯一的差别,仅在于它不会套用母版页面,其它则完全相同。
@Html.Action(「GetPartialView」);
通过 Html.Action 与 Html.Partial 载入分部视图结果是一样的,但载入的过程却差别很大。若使用 Html.Partial 载入分部视图是通过 HtmlHelper 直接读取 *.cshtml 文件,直接执行该视图并取得结果。若使用 Html.Action 的话,则会通过 HtmlHelper 对 IIS 再进行一次处理要求(通过 Server.Execute 方法),因此,使用 Html.Action 会重新执行一遍 Controller 的生命周期
❡ 模型校验
❡ Models/StudentBo.cs
1 |
|
❡ Models/StudentEditBo.cs
1 |
|
❡ Student/Add.cshtml
1 |
|
❡ Student/Edit.cshtml
1 |
|
❡ Profiles/StudentProfile.cs
1 |
|
❡ StudentController.cs
1 |
|
❡ 模型绑定
❡ Apifox 软件
1 |
|
1 |
|
1 |
|
❡ Models/BindTest.cs
1 |
|
❡ Models/ProductInput.cs
1 |
|
❡ Controllers/ProductController.cs
1 |
|
❡ Controllers/BindController.cs
1 |
|
❡ Views/Product/Create.cshtml
1 |
|
❡ 文件上传
❡ UploadController.cs
1 |
|
❡ Views/upload/index.cshtml
1 |
|
❡ Views/upload/LargeFile.cshtml
1 |
|
❡ Views/upload/MoreUpload.cshtml
1 |
|
❡ 结合表单上传文件
❡ ProductController.cs
1 |
|
❡ Views/product/create.cshtml
1 |
|
❡ productinfo.cs
1 |
|
❡ 特殊视图
❡ _Layout.cshtml 布局页
1 |
|
❡ ** _ViewStart.cshtml** 视图
1 |
|
❡ _ViewImport.cshtml 命名导入视图
1 |
|
❡ 标签助手
标签助手是服务端代码能够参与在 Razor 文件中创建和呈现 HTML 元素。例如,内置的 ImageTagHelper 可以将版本号追加到图像名称。无论何时更改图像,服务器都会为图像生成新的唯一版本,因此可以保证客户端获取当前图像(而不是过时的缓存图像)。内置的标签助手多用于常见任务,例如创建表单,链接和加载资源等。标签助手是在 C# 中定义的,它们基于元素名称,属性名称或父标签来定位 HTML 元素。例如,当应用 LabelTagHelper 特性时,内置的 LabelTagHelper 可以减少 Razor 视图中 HTML**** 和 C# 之间的 **** 显示转换。
1 |
|
❡ Student/Add.cshtml
1 |
|
❡ Student/index.cshtml
1 |
|
❡ Models/Student.cs
1 |
|
❡ StudentController.cs
1 |
|
❡ Shared/_Layout.cshtml
1 |
|
❡ tags/EmailTagHelper.cs
1 |
|
❡ Views/_ViewImports.cshtml
1 |
|
❡ ViewComponents/StudentViewComponent.cs
1 |
|
❡ Program.cs
1 |
|
❡ 服务与配置
❡ 以前
1 |
|
❡ 现在
1 |
|
❡ 正转
1 |
|
❡ 反转
把依赖的创建丢给其它人,自己只负责使用,其它人丢给你依赖的这个过程理解为注入。
为了在业务变化的时候尽少改动代码可能造成的问题。
1 |
|
❡ 何为容器
❡ 容器负责两件事情:
- 绑定服务与实例之间的关系
- 获取实例,并对实例进行管理(创建与销毁)
❡ ASP.NET CORE DI
使用时通过 Nuget 包 Microsoft.Extensions.DependencyInjection 来使用 。
相对与其他 DI 框架,它比较轻量,且只支持构造器注入,不支持属性注入、以及方法注入。
在.NET Core 中 DI 的核心分为两个组件:IServiceCollection 和 IServiceProvider
- IServiceCollection 负责注册
- IServiceProvider 负责提供实例
❡ 服务的注册方式
1 |
|
❡ 服务提供者 ServiceProvider
服务实例的创建、销毁均由 ServiceProvider 负责维护,生成的服务实例一般不要执行对象的 Dispose 方法(如果有的话),因为服务实例有可能被其他地方使用。
当释放 ServiceProvider 时,该 Provider 将会释放由它生成的对象。
在生成实例时,如果对象实现了 Dispose 接口,它会把该对象保存在 disposables 列表中,当 Provider 释放时,当即会自动调用 disposables 元素对象的 Dispose 方法而 ServiceProvider 本身又可以创建新的 ServiceProvider,这样就相当生成了一个作用域 Scope。
❡ 获取的服务的三种方式
1 |
|
❡ 服务的生命周期
生命周期分为三种:
瞬态 Transient: 每一次从 ServiceProvider 取得,均返回一个新的实例。
请求获取 -(GC 回收 - 主动释放) 每一次获取的对象都不是同一个
作用域 Scope:在同一作用域下生成的实例相同,即同一个 ServiceProvider 下取得的实例相同
请求开始 - 请求结束 在这次请求中获取的对象都是同一个
单例 Singleton: 任意一个 ServiceProvider 下取得的实例都相同
项目启动 - 项目关闭 相当于静态类 只会有一个
作用域主要针对特定 ServiceProvider 的,即在同一个 ServiceProvider 下,注册为 Scope 类型的对象为单例
❡ program.cs
1 |
|
❡ HomeController.cs
1 |
|
❡ Options/RedisOptions.cs
1 |
|
❡ Services/IFoodService.cs
1 |
|
❡ Services/Impl/FoodService.cs
1 |
|
❡ Views/Home/Index.cshtml
1 |
|
❡ Autofac
❡ Nuget 安装相关包
1 |
|
❡ AutofacModuleRegister.cs
1 |
|
❡ program.cs
1 |
|
❡ 生命周期
1 |
|
❡ 系统内置服务
❡ 内置的日志服务
❡ program.cs
1 |
|
1 |
|
1 |
|
❡ Log4Net 使用
❡ 安装 2 个包
1 |
|
❡ log4net.config
1 |
|
❡ Program.cs
1 |
|
❡ NLog 日志
属性变为始终复制以及内容便于发布的时候
❡ 安装包
1 |
|
❡ Program.cs
1 |
|
❡ nlog.config
1 |
|
❡ SeriLog(推荐使用)
默认级别 - 如果没有指定 MinimumLevel ,则将处理 Information 级别事件和更高级别的事件。
❡ 安装包
1 |
|
❡ appsettings.json
1 |
|
❡ Program.cs
1 |
|
❡ 日志扩展
❡ 输出到文件需要引用一个包
1 |
|
❡ Program.cs
1 |
|
❡ appsettings.json
1 |
|
❡ Configuration 配置
❡ HomeController/Index
1 |
|
❡ Program.cs
1 |
|
❡ appsettings.Development.json
1 |
|
❡ Options\RedisOptions.cs
1 |
|
❡ 结合 EntityFramework
❡ 环境搭建
❡ 创建类库
1 |
|
❡ 创建数据库
1 |
|
❡ 创建类
❡ WebApplication_MVC_EFCore_Model/BaseEntity.cs
1 |
|
❡ WebApplication_MVC_EFCore_Model/Debtor.cs
1 |
|
WebApplication_MVC_EFCore_Model/Account.cs
1 |
|
❡ 安装包
1 |
|
❡ appsettings.json
1 |
|
❡ WebApplication_MVC_EFCore_Service/StepDbContext.cs
1 |
|
❡ Program.cs
1 |
|
❡ 分页显示
❡ Service/Dto/AccountViewModel.cs
1 |
|
❡ Service/Dto/condition/AccountRequest.cs
1 |
|
❡ Service/Dto/condition/PageRequest.cs
1 |
|
❡ Service/IAccountService.cs
1 |
|
❡ Service/AccountService.cs
1 |
|
❡ 向 Service 导入 util 包
❡ AccountController
1 |
|
❡ Views/Account/Index.cshtml
1 |
|
❡ 查询显示
❡ AccountController
1 |
|
❡ Views/Account/Index.cshtml
1 |
|
❡ Step4.Unit7.Service\AccountService.cs
1 |
|
❡ 增删改查
❡ AccountController.cs
1 |
|
❡ Step4.Unit7.Service\Profiles\AccountProfile.cs
1 |
|
❡ Step4.Unit7.Service\Dto\AccountBo.cs
1 |
|
❡ Step4.Unit7.Service\Dto\DebtorViewModel.cs
1 |
|
❡ Step4.Unit7.Service\Dto\AccountUpdateBo.cs
1 |
|
❡ Step4.Unit7\Step4.Unit7.Model\Debtor.cs
1 |
|
❡ Step4.Unit7.Model\Account.cs
1 |
|
❡ Step4.Unit7.Model\BaseEntity.cs
1 |
|
❡ Step4.Unit7\Views\Account\Index.cshtml
1 |
|
❡ Step4.Unit7\Views\Account\Add.cshtml
1 |
|
❡ Step4.Unit7\Views\Account\Edit.cshtml
1 |
|
❡ Program.cs
1 |
|
❡ Step4.Unit7.Service\IAccountService.cs
1 |
|
❡ Step4.Unit7.Service\AccountService.cs
1 |
|
❡ 角色与授权
❡ Identity 完成登录
- 登录与注册的 View
- 用于登录的控制器 AccountController
- Model 校验
❡ appsettings.json
1 |
|
❡ 安装包
1 |
|
❡ Program.cs
1 |
|
❡ 数据迁移
❡ Visual Studio 2022
1 |
|
❡ CLI 命令行
1 |
|
❡ 登录与注册功能
❡ Models\LoginViewModel.cs
1 |
|
❡ Models\RegisterViewModel.cs
1 |
|
❡ Views\Account\Login.cshtml
1 |
|
❡ Views\Account\Register.cshtml
1 |
|
❡ Program.cs
1 |
|
❡ Controllers\AccountController.cs
1 |
|
❡ 相关配置
❡ Controllers\AccountController.cs
1 |
|
❡ Controllers\HomeController.cs
1 |
|
❡ Views\Shared_Layout.cshtml
1 |
|
❡ Program.cs
1 |
|
❡ 用户配置
❡ Controllers\UserController.cs
1 |
|
❡ Views\User\Index.cshtml
1 |
|
❡ Views\Shared_Layout.cshtml
1 |
|
❡ Models\UserEditViewModel.cs
1 |
|
❡ 角色管理
❡ Program.cs
1 |
|
❡ Controllers\RoleController.cs
1 |
|
❡ Models\RoleCreateViewModel.cs
1 |
|
❡ Models\RoleEditViewModel.cs
1 |
|
❡ Views\Role\Create.cshtml
1 |
|
❡ Views\Role\Edit.cshtml
1 |
|
❡ Views\Role\Index.cshtml
1 |
|
❡ Views\Shared_Layout.cshtml
1 |
|
❡ 用户角色管理
❡ Controllers\UserController.cs
1 |
|
❡ Views\User\Index.cshtml
1 |
|
❡ 授权策略
❡ 基于角色授权
❡ Program.cs
1 |
|
❡ Controllers\RoleController.cs
1 |
|
❡ 基于 Claim 授权(部分授权)
❡ Program.cs
1 |
|
❡ Controllers\AccountController.cs
1 |
|
❡ Views\User\Index.cshtml
1 |
|
❡ 面向切面编程
❡ 定义
无感增强功能,不影响原先的业务
❡ Controllers\UserController.cs
1 |
|
❡ XSS 攻击
❡ Controllers\XssController.cs
1 |
|
❡ Views\Xss\Index.cshtml
1 |
|
❡ CSRF 攻击
❡ Controllers\CsrfController.cs
1 |
|
❡ Models\StudentViewModel.cs
1 |
|
❡ Views\Csrf\Index.cshtml
1 |
|
❡ Program.cs
1 |
|
❡ 其他系统过滤器
❡ Controllers\UserController.cs
1 |
|
❡ 授权过滤器
❡ Filters\CustomerAuthorizeAttribute.cs
1 |
|
❡ Controllers\AccountController.cs
1 |
|
❡ Models\LoginViewModel.cs
1 |
|
❡ Views\Shared_Layout.cshtml
1 |
|
❡ Views\Account\Login.cshtml
1 |
|
❡ appsettings.json
1 |
|
❡ 安装包
1 |
|
❡ Program.cs
1 |
|
❡ Controllers\UserController.cs
1 |
|
❡ 异常过滤器
❡ Controllers\HomeController.cs
1 |
|
❡ Filters\CustomerExceptionFilterAttribute.cs
1 |
|
❡ 过滤器的应用方式
❡ Filters\CustomerExceptionFilterAttribute.cs
1 |
|
❡ Controllers\HomeController.cs
1 |
|
❡ 资源过滤器
❡ Controllers\ResourceController.cs
1 |
|
❡ Filters\CustomerResourceFilterAttribute.cs
1 |
|
❡ Views\Resource\Index.cshtml
1 |
|
❡ 行为过滤器
❡ Filters\CustomerActionFilterAttribute.cs
1 |
|
❡ Areas\Mobile\Controllers\ProductController.cs
1 |
|
❡ Areas\Mobile\Views\Product\Index.cshtml
1 |
|
❡ Areas\Pc\Controllers\ProductController.cs
1 |
|
❡ Areas\Pc\Views\Product\Index.cshtml
1 |
|
❡ Program.cs
1 |
|
❡ 结果过滤器
❡ Filters\CustomerActionResultFilterAttribute.cs
1 |
|
❡ 拦截器的使用
❡ 安装包
1 |
|
❡ Program.cs
1 |
|
❡ AutofacRegisterModule.cs
1 |
|
❡ Interceptors\LogAop.cs
1 |
|
❡ Services\IStudentService.cs
1 |
|
❡ Services\StudentService.cs
1 |
|
❡ Controllers\HomeController.cs
1 |
|
❡ 项目发布
❡ IIS 部署
❡ Linux 部署
❡ Linux-docker
❡ Step4.Unit8\Dockerfile
1 |
|
1 |
|
❡ RESTful API
❡ Ado.net
1 |
|
❡ 构建 DataTable
1 |
|
❡ 配置文件
1 |
|
❡ 数据连接池
1 |
|
❡ 参数化查询
1 |
|
❡ 存储过程
1 |
|
❡ 输出参数
1 |
|
❡ Web 项目操作 Ado (下面三个以及这个都需要学习)
1 |
|
❡ 视频操作
1 |
|
❡ index.html 操作
1 |
|
❡ 封装 DbHelper
1 |
|
❡ 数据访问技术 (ORM)
❡ FreeSQL/SQLSugger
❡ EntityFrameworkCore
❡ ORM 介绍
❡ 安装.Net CLI
❡ EFCore 入门
❡ QuackStart\BloggingContext.cs
1 |
|
❡ QuackStart\Blog.cs
1 |
|
❡ QuackStart\Post.cs
1 |
|
❡ Program.cs
1 |
|
❡ 安装包
1 |
|
❡ DbContext 在 mvc 运行
❡ 安装包
1 |
|
❡ appsettings.json
1 |
|
❡ Program.cs
1 |
|
❡ BlogContext.cs
1 |
|
❡ Data\Blog.cs
1 |
|
❡ Data\Post.cs
1 |
|
❡ Controllers\HomeController.cs
1 |
|
❡ DbContext 几种创建方式(总共三种,还有之前那一种方式)
❡ 第一种方式
1 |
|
❡ 第二种方式
❡ HomeController.cs
1 |
|
❡ BlogContext.cs
1 |
|
❡ DbContext 工厂
❡ Program.cs
1 |
|
❡ Views\FactoryContext\Index.cshtml
1 |
|
❡ Controllers\FactoryContextController.cs
1 |
|
❡ 一个项目中操作多种数据库 (有点问题)
❡ 安装包
1 |
|
❡ Program.cs
1 |
|
❡ MySqlBlogContext.cs
1 |
|
❡ Data\Author.cs
1 |
|
❡ DbContext 连接池
❡ Program.cs
1 |
|
❡ Controllers\ContextPoolController.cs
1 |
|
❡ FluentApi 更新表名
❡ Data\Post.cs
1 |
|
❡ Data\Blog.cs
1 |
|
❡ MySqlContext.cs
1 |
|
1 |
|
❡ SqlContext.cs
1 |
|
❡ FluentApi 更新 Schema
1 |
|
❡ FluentApi 表注释
1 |
|
❡ FluentApi 排除表迁移
1 |
|
❡ FluentApi 属性配置
1 |
|
❡ FluentApi 分组配置
1 |
|
❡ EntityConfig\BlogEntityConfig.cs
1 |
|
❡ FluentApi 主键设置
❡ EntityConfig\AuthorEntityConfig.cs
1 |
|
❡ FluentApi 默认值操作 / 值自动更新
❡ EntityConfig\TagEntityConfig.cs
1 |
|
❡ FluentApi 值自动更新
1 |
|
❡ 保存数据
❡ 增删改查
1 |
|
❡ SaveChanges 用法
多个操作的时候可以写一个,因为是事务,但是如果其中有一个有错误,所有的都会出现回滚都不执行.
❡ 保存相关数据(包含其他属性)
1 |
|
❡ 并发问题
1 |
|
❡ 事务
1 |
|
❡ Dapper
❡ NHibernate
❡ 版本控制系统
❡ Git
❡ 背景
❡ 集中式
SVN:集中式版本控制系统,与 Git 相比,不支持本地化开发,但故障恢复速度较快,可以像 Git
一样实现版本回退。
集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,
所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。
中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完
了,再放回图书馆。
❡ 分布式
Git:现在应用最为广泛的分布式版本控制系统,使用广泛,由于分布式的特点,支持本地化开发
和离线工作。
分布式版本控制系统根本没有 “中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库。
❡ 操作
❡ Git 环境配置
❡ 基本操作
❡ 常用指令配置别名(可选)
❡ 获取本地仓库
❡ 基本操作指令
❡ 工作区(Working Directory)
git_test 就是所谓的工作区
❡ 版本库(Repository)
工作区有一个隐藏目录 .git ,这个不算工作区,而是 Git 的版本库。
Git 的版本库里存了很多东西,其中最重要的就是称为 stage(或者叫 index)的暂存区,还有 Git 为我们
自动创建的第一个分支 master ,以及指向 master 的一个指针叫 HEAD 。
❡ 查看提交日志 **(log)**
format ,可以定制记录的显示格式
❡ 版本回退 **/** 版本穿梭
文件回退
1 |
|
仓库版本回退
作用:版本切换
命令形式:git reset --hard commitID(版本 id)
commitID 可以使用 git-log 或 git log 指令查看
git reflog
❡ 添加文件至忽略列表
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动
生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以在工作目录
中创建一个名为 .gitignore 的文件(文件名称固定),列出要忽略的文件模式。下面是一个示例(.net
常用的):
.Net 忽略文件
1 |
|
Java 忽略文件
1 |
|
❡ 练习 **😗* 基础操作
1 |
|
❡ 分支
1 |
|
❡ 解决冲突(难点)
❡ 合并提交
在实际开发中,可能一个功能有会 N 次提交记录,甚至有时候给每次提交取名称都变成一件脑壳疼的问
题,并且这种 “重复性” 的提交如果大家都推送至远端仓库,会导致远端仓库的提交记录密密麻麻,不易
梳理。其实我们可以把这些 “重复性” 的提交合并为一次提交记录再推送至远端仓库。
** 语法:****git rebase -i HEAD~** 最近几次提交次数
1 |
|
里面的提示有:
pick:保留该 commit(缩写:p)
reword:保留该 commit,但我需要修改该 commit 的注释(缩写:r)
edit:保留该 commit, 但我要停下来修改该提交 (不仅仅修改注释)(缩写:e)
squash:将该 commit 和前一个 commit 合并(缩写:s)
fixup:将该 commit 和前一个 commit 合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行 shell 命令(缩写:x)
drop:我要丢弃该 commit(缩写:d)
❡ 冲突解决
在 git rebase 过程中,可能会存在冲突,此时就需要解决冲突。
错误提示信息: git rebase -i resumeerror: could not apply … 。
1 |
|
❡ 开发中分支使用原则与流程
几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离
开来进行重大的 Bug 修改、开发新的功能,以免影响开发主线。
在开发中,一般有如下分支使用原则与流程:
master (生产) 分支
线上分支,主分支,中小规模项目作为线上运行的应用对应的分支;
develop**(开发)分支 **
是从 master 创建的分支,一般作为开发部门的主要开发分支,如果没有其他并行开发不同期上线
要求,都可以在此版本进行开发,阶段开发完成后,需要是合并到 master 分支,准备上线。
feature/xxxx 分支
从 develop 创建的分支,一般是同期并行开发,但不同期上线时创建的分支,分支上的研发任务完成后
合并到 develop 分支。
hotfix/xxxx 分支
从 master 派生的分支,一般作为线上 bug 修复使用,修复完成后需要合并到 master、test、
develop 分支。
还有一些其他分支,在此不再详述,例如 test 分支(用于代码测试)、pre 分支(预上线分支)等等。
❡ 练习 **😗* 分支操作
git init 后需要提交一次后才能创建分支
1 |
|
❡ Git**** 远程仓库
❡ 如何搭建 git 远程仓库
比较常用的有 GitHub、码云、GitLab 等。
gitHub( 地址:https://github.com/ )是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作
为唯一的版本库格式进行托管,故名 gitHub
码云(地址: https://gitee.com/ )是国内的一个代码托管平台,由于服务器在国内,所以相比于
GitHub,码云速度会更快
GitLab (地址: https://about.gitlab.com/ )是一个用于仓库管理系统的开源项目,使用 Git 作 为代码
管理工具,并在此基础上搭建起来的 web 服务,一般用于在企业、学校等内部网络搭建 git 私服。
❡ 配置 SSH 公钥
作用:用于验证操作者的身份信息(相当于用户登录)。
❡ 操作远程仓库
❡ 推送到远程仓库
命令:git push [-f] [–set-upstream] [远端名称 [本地分支名][: 远端分支名] ]
如果远程分支名和本地分支名称相同,则可以只写本地分支
git push origin master
n -f 表示强制覆盖
n --set-upstream 推送到远端的同时并且建立起和远端分支的关联关系。
git push --set-upstream origin master:dev
注意:在 ****push 之前 一定要先 pull (拉取), 获取最新代码之后,解决冲突后(如果有冲突的话),才能 push 成功!!!
如果当前分支已经和远端分支关联,则可以省略分支名和远端名。
git push 将 master 分支推送到已关联的远端分支。
❡ 本地分支与远程分支的关联关系
查看关联关系我们可以使用 git branch -vv 命令
❡ 从远程仓库克隆
如果已经有一个远端仓库,我们可以直接 clone 到本地。
命令: git clone <仓库路径> [本地目录]
本地目录可以省略,会自动生成一个目录
❡ 从远程仓库中抓取和拉取
远程分支和本地的分支一样,我们可以进行 merge 操作,只是需要先把远端仓库里的更新都下载到本
地,再进行操作。
抓取 命令:git fetch [remote name] [branch name]
Ø 抓取指令就是将仓库里的更新都抓取到本地,不会进行合并
Ø 如果不指定远端名称和分支名,则抓取所有分支。
Ø 拉取 命令:git pull [remote name] [branch name]
Ø git pull --rebase origin master(用在合并代码的时候其作用就是在一个随机创建的分支上处理冲
突,避免了直接污染原来的分区)
例如:git pull origin dev
Ø 拉取指令就是将远端仓库的修改拉到本地并自动进行合并,等同于 fetch+merge
Ø 如果不指定远端名称和分支名,则抓取所有并更新当前分支。
❡ 练习 **😗* 远程仓库操作
1 |
|
❡ 开发工具结合 git (还有点操作问题)
Git 提交常见问题
- husky > commit-msg (node v14.16.0)
⧗ input: 你的消息 xxxx…
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
原因:你提交的消息不符合规范
解决办法:,需要在前面加 feat: 。如果是 bug, 则前面加 bug: 。注意,冒号(英文)后面有空格
- hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 『git pull …』) before pushing again.
原因分析:
是由于本地和远程仓库两者代码文件不同步,因此需要先 pull,进行合并然后再进行 push
解决方法:
1、先使用 pull 命令:
git pull --rebase origin master
2、再使用 push 命令:
git push -u origin master
❡ Svn
❡ maven
❡ 云与容器
❡ Azure 或其他云服务
❡ Docker
❡ 设计模式
❡ 微服务架构
❡ 项目篇
❡ 幸福旅游网
❡ 导入数据库
第一种打开并复制 (小数据可以,大数据的话就得使用命令行的方式),第二种使用环境变量设置并通过命令的方式进行导入
1 |
|
❡ 数据库设计
❡ 为什么需要大图和小图的地址?
因为如果使用了 css 缩放技术,还是原来的大小 2M,大图加小图一块是 4M. 如果采取两个字段分别存储大图和小图的地址则是 2M+20k 的大小,大大提升了速度。
❡ 为什么需要 Version?
是通过乐观锁来控制并发人数的,会限制人数
❡ 索引设计
1 |
|
❡ 依赖组件安装
❡ 安装 docker
通过 CentOS7 安装 docker
1 |
|
❡ 安装 redis
1 |
|
❡ 安装 rabbitmq
1 |
|
❡ 项目搭建
创建 asp.net core 空的项目,并且创建三个类库 travel.data、travel.service、travel.CommonUtil,并且添加对应的引用包
❡ travel.CommonUtil
1 |
|
❡ travel.data
1 |
|
❡ 反向工程
删除无参构造函数以及一些暂时用不上的
❡ DDD 项目结构
是四层结构
表示层:为用户提供接口。使用应用层实现与用户交互.
应用层:表示层与领域层的中介,编排业务对象执行特定的应用程序任务。使用应用程序逻辑实现用例.
领域层:包含业务对象以及业务规则。是应用程序的核心.
基础设施层:提供通用的技术功能,支持更高的层,主要使用第三方类库
❡ 配置连接池
1 |
|
❡ 组件配置
❡ SeriLog 日志
❡ travel.api
1 |
|
1 |
|
❡ 配置 Autofac
1 |
|
❡ 新建一个 AutofacModuleRegister(名称随意)继承 Autofac.Module 重写 Load 方法
1 |
|
❡ Program.cs 类中将 DI 替换为 Autofac
1 |
|
❡ 配置 AutoMap
1 |
|
1 |
|
❡ 配置 Swagger
1 |
|
❡ travel.api 和 travel.data
启用 XML 注释
❡ 注册 Swagger 服务
1 |
|
1 |
|
❡ 隐藏某些接口
1 |
|
❡ 配置 JWT
1 |
|
1 |
|
1 |
|
1 |
|
❡ 生成 Token
1 |
|
Token 自动刷新
1 |
|
❡ 配置 Redis
1 |
|
❡ 即时通讯
❡ travel.api/Hubs
1 |
|
❡ travel.api/Consts
1 |
|
❡ Program.cs
1 |
|
❡ 事件总线
1 |
|
❡ 注册服务
1 |
|
❡ WebAPI 设置
❡ 统一返回格式
1 |
|
❡ JSON 序列化
1 |
|
❡ program.cs
1 |
|
❡ 异常处理器
1 |
|
❡ 授权过滤器
1 |
|
❡ travel.api/controllers
1 |
|
❡ program.cs
1 |
|
❡ 设置授权策略
1 |
|
❡ program.cs
1 |
|
❡ 设置基类
1 |
|
❡ 设置跨域
1 |
|
❡ 公共上传
❡ 支持静态资源
1 |
|
❡ 在根目录新建固定文件夹: wwwroot
❡ travel.api/controllers
1 |
|