目 录
第I部分 C#和.NET 6简介
第1章 C#和.NET 6基础 3
1.1 .NET平台的一些重要优势 3
1.2 理解.NET支持周期 4
1.3 .NET平台的组成模块概述 4
1.3.1 基类库的作用 5
1.3.2 C#带来了什么 5
1.3.3 托管与非托管代码 6
1.4 使用.NET支持的其他编程语言 7
1.5 .NET程序集概述 7
1.5.1 CIL的作用 7
1.5.2 CIL的优势 10
1.5.3 将CIL编译为特定于平台的指令 10
1.5.4 .NET类型元数据的作用 10
1.5.5 程序集清单的作用 11
1.6 理解公共类型系统 12
1.6.1 CTS类类型 12
1.6.2 CTS接口类型 13
1.6.3 CTS结构类型 13
1.6.4 CTS枚举类型 13
1.6.5 CTS委托类型 14
1.6.6 CTS类型成员 14
1.6.7 CTS固有数据类型 14
1.7 理解公共语言规范 15
1.8 理解.NET运行时 17
1.9 区分程序集、名称空间和类型 17
1.9.1 在代码中访问名称空间 19
1.9.2 全局using语句(10.0新增) 19
1.9.3 文件作用域的名称空间(10.0新增) 21
1.9.4 引用外部程序集 21
1.10 使用ildasm.exe探索程序集 21
1.11 小结 22
第2章 构建C#应用程序 23
2.1 安装.NET 6 23
2.1.1 理解.NET版本号 23
2.1.2 确认.NET 6安装成功 23
2.2 使用Visual Studio构建.NET Core应用程序 25
2.2.1 安装Visual Studio 2022 (Windows) 25
2.2.2 试用Visual Studio 2022 26
2.3 使用Visual Studio Code构建.NET Core应用程序 35
2.4 找到.NET Core和C#的文档 38
2.5 小结 38
第II部分 核心C#编程
第3章 C#的核心编程结构(I) 41
3.1 分解一个简单的C#程序(C# 10更新) 41
3.1.1 使用Main()方法的不同形式(7.1更新) 43
3.1.2 使用顶级语句(9.0新增) 44
3.1.3 指定应用程序错误代码(9.0更新) 45
3.1.4 处理命令行实参(9.0更新) 47
3.1.5 在Visual Studio 2022中指定命令行实参 48
3.2 System.Environment类中的其他成员(10.0更新) 49
3.3 使用System.Console类 50
3.3.1 使用Console类执行基本的输入和输出(I/O)操作 51
3.3.2 设置控制台输出的格式 52
3.3.3 设置数值数据的格式 53
3.3.4 在控制台应用程序之外设置数值数据的格式 54
3.4 使用系统数据类型和对应的C#关键字 54
3.4.1 理解变量声明和初始化 55
3.4.2 使用固有数据类型和new操作符(9.0更新) 56
3.4.3 理解数据类型的类层次 57
3.4.4 理解数值数据类型的成员 59
3.4.5 理解System.Boolean的成员 59
3.4.6 理解System.Char的成员 59
3.4.7 解析字符串数据的值 60
3.4.8 使用TryParse从字符串数据解析值 60
3.4.9 使用System.DateTime和System.Timespan(10.0更新) 61
3.4.10 使用System.Numerics名称空间 62
3.4.11 使用数字分隔符(7.0新增) 63
3.4.12 使用二进制字面值(7.0/7.2新增) 63
3.5 使用字符串数据 64
3.5.1 执行基本字符串操作 64
3.5.2 执行字符串连接 65
3.5.3 使用转义字符 65
3.5.4 执行字符串插值 66
3.5.5 定义原样字符串(8.0更新) 69
3.5.6 字符串的相等性 69
3.5.7 String是不可变的 71
3.5.8 使用System.Text.StringBuilder类型 73
3.6 缩窄和加宽数据类型转换 73
3.6.1 使用checked关键字 75
3.6.2 设置项目级别的溢出检查(项目文件) 77
3.6.3 设置项目级别的溢出检查(Visual Studio) 77
3.6.4 使用unchecked关键字 78
3.7 理解隐式类型的局部变量 78
3.7.1 隐式声明数字 80
3.7.2 理解隐式类型的变量的限制 80
3.7.3 隐式类型的数据是强类型的数据 81
3.7.4 理解隐式类型的局部变量的有用性 82
3.8 使用C#的迭代结构 82
3.8.1 使用for循环 83
3.8.2 使用foreach循环 83
3.8.3 在foreach结构中使用隐式类型 84
3.8.4 使用while和do/while循环结构 84
3.9 作用域简介 85
3.10 使用决策结构和关系/相等性操作符 85
3.10.1 使用if/else语句 86
3.10.2 使用相等性和关系操作符 86
3.10.3 在if/else中使用模式匹配(7.0新增) 87
3.10.4 模式匹配的改进(9.0新增) 87
3.10.5 使用条件操作符(7.2、9.0更新) 88
3.10.6 使用逻辑操作符 89
3.10.7 使用switch语句 90
3.10.8 执行switch语句的模式匹配(7.0新增,9.0更新) 92
3.10.9 使用switch表达式(8.0新增) 94
3.11 小结 96
第4章 C#的核心编程结构(II) 97
4.1 理解C#的数组 97
4.1.1 C#的数组初始化语法 98
4.1.2 理解隐式类型的局部数组 99
4.1.3 定义一个Object数组 99
4.1.4 使用多维数组 100
4.1.5 将数组用作实参或返回值 101
4.1.6 使用System.Array基类 102
4.1.7 使用索引和范围(8.0新增,10.0更新) 103
4.2 理解方法 105
4.2.1 理解表达式体成员 105
4.2.2 理解局部函数(7.0新增,9.0更新) 106
4.2.3 理解静态局部函数(8.0新增) 107
4.3 理解方法参数 107
4.3.1 理解方法参数的修饰符 108
4.3.2 理解默认的参数传递行为 108
4.3.3 使用out修饰符(7.0更新) 109
4.3.4 使用ref修饰符 111
4.3.5 使用in修饰符(7.2新增) 111
4.3.6 使用params修饰符 112
4.3.7 定义可选参数 113
4.3.8 使用命名实参(7.2更新) 114
4.3.9 理解方法重载 115
4.3.10 检查参数是否为null(10.0更新) 118
4.4 理解枚举类型 118
4.4.1 控制枚举的底层存储 119
4.4.2 声明枚举变量 120
4.4.3 使用System.Enum类型 121
4.4.4 动态发现枚举的名称-值对 121
4.4.5 使用枚举、标志和位操作 122
4.5 理解结构 124
4.5.1 创建结构变量 125
4.5.2 结构的构造函数(10.0更新) 126
4.5.3 使用字段初始值(10.0新增) 127
4.5.4 使用只读结构(7.2新增) 127
4.5.5 使用只读成员(8.0新增) 128
4.5.6 使用ref结构(7.2新增) 129
4.5.7 使用可释放的ref结构(8.0新增) 129
4.6 理解值类型和引用类型 130
4.6.1 使用值类型、引用类型和赋值操作符 130
4.6.2 使用包含引用类型的值类型 132
4.6.3 按值传递引用类型 133
4.6.4 按引用传递引用类型 135
4.6.5 关于值类型和引用类型的一些细节 135
4.7 理解C#的可空类型 136
4.7.1 使用可空值类型 137
4.7.2 使用可空引用类型(8.0新增,10.0更新) 138
4.7.3 处理可空类型 141
4.8 理解元组(7.0新增/更新) 143
4.8.1 开始使用元组 143
4.8.2 使用推断的变量名称(7.1更新) 144
4.8.3 理解元组的相等性/不相等性(7.3新增) 144
4.8.4 理解作为方法返回值的元组 145
4.8.5 理解元组中使用的丢弃操作符 145
4.8.6 理解switch表达式的元组模式匹配(8.0新增) 145
4.8.7 解构元组(10.0更新) 146
4.9 小结 148
第III部分 使用C#进行面向对象编程
第5章 理解封装 151
5.1 C#的类类型简介 151
5.2 理解构造函数 153
5.2.1 理解默认构造函数的作用 154
5.2.2 定义自定义构造函数 154
5.2.3 继续介绍默认构造函数 156
5.3 理解this关键字的作用 157
5.3.1 使用this链接构造函数调用 158
5.3.2 观察构造函数的执行流 160
5.3.3 可选实参示例 162
5.4 理解static关键字 162
5.4.1 定义静态字段数据 163
5.4.2 定义静态方法 165
5.4.3 定义静态构造函数 166
5.4.4 定义静态类 168
5.4.5 通过C#的using关键字导入静态成员 168
5.5 定义面向对象编程的支柱 169
5.5.1 理解封装的作用 169
5.5.2 理解继承的作用 170
5.5.3 理解多态性的作用 171
5.6 理解C#的访问修饰符(7.2更新) 172
5.6.1 使用默认访问修饰符 173
5.6.2 使用访问修饰符和嵌套类型 173
5.7 理解第一个支柱:C#的封装服务 174
5.7.1 使用传统的访问器和修改器实现封装 175
5.7.2 使用属性进行封装 177
5.7.3 在类定义内使用属性 179
5.7.4 只读属性 181
5.7.5 只写属性 181
5.7.6 在属性中混合使用私有的和公有的get/set方法 182
5.7.7 再次讨论static关键字:定义静态属性 182
5.7.8 使用属性模式进行模式匹配(8.0新增) 182
5.7.9 展开的属性模式(10.0新增) 184
5.8 理解自动属性 185
5.8.1 使用自动属性 186
5.8.2 自动属性和默认值 187
5.8.3 初始化自动属性 188
5.9 理解对象初始化 189
5.9.1 查看对象初始化语法 189
5.9.2 使用仅初始化设置器(9.0新增) 190
5.9.3 使用初始化语法调用自定义构造函数 191
5.9.4 使用初始化语法初始化数据 192
5.10 使用常量字段数据和只读字段数据 193
5.10.1 理解常量字段数据 193
5.10.2 理解只读字段 194
5.10.3 理解静态只读字段 195
5.11 理解分部类 195
5.12 记录(9.0新增) 197
5.12.1 使用标准属性语法的不可变记录类型 198
5.12.2 使用位置语法的不可变记录类型 199
5.12.3 可变记录类型 200
5.12.4 记录类型的值相等性 200
5.12.5 使用with表达式复制记录类型 201
5.13 记录结构(10.0新增) 202
5.13.1 可变的记录结构 202
5.13.2 不可变的记录结构 203
5.13.3 解构记录结构 203
5.14 小结 203
第6章 理解继承和多态性 205
6.1 理解继承的基本机制 205
6.1.1 指定现有类的父类 206
6.1.2 多基类 207
6.1.3 使用sealed关键字 208
6.2 回顾Visual Studio的类图 208
6.3 理解OOP的第二个支柱:继承的细节 210
6.3.1 使用base关键字调用基类的构造函数 211
6.3.2 保守家族秘密:protected关键字 213
6.3.3 添加密封类 213
6.3.4 理解记录类型的继承(9.0新增) 214
6.4 包含/委托编程 217
6.5 理解OOP的第三个支柱:C#对多态性的支持 220
6.5.1 使用virtual和override关键字 220
6.5.2 在Visual Studio/ Visual Studio Code中重写虚拟方法 223
6.5.3 密封虚拟成员(10.0更新) 223
6.5.4 理解抽象类 224
6.5.5 理解多态接口 225
6.5.6 理解成员隐藏 229
6.6 理解基类/派生类的强制转换规则 230
6.6.1 使用C#的as关键字 232
6.6.2 使用C#的is关键字(7.0、9.0更新) 233
6.6.3 继续讨论模式匹配(7.0新增) 235
6.7 理解超级父类:System.Object 236
6.7.1 重写System.Object.ToString() 238
6.7.2 重写System.Object.Equals() 238
6.7.3 重写System.Object.GetHashCode() 239
6.7.4 测试修改后的Person类 240
6.7.5 使用System.Object的静态成员 241
6.8 小结 242
第7章 理解结构化异常处理 243
7.1 错误、bug和异常 243
7.2 .NET异常处理的作用 244
7.2.1 .NET异常处理的构造块 244
7.2.2 System.Exception基类 245
7.3 最简单的示例 246
7.3.1 抛出通用异常 248
7.3.2 捕获异常 249
7.3.3 将throw作为表达式(7.0新增) 250
7.4 配置异常的状态 250
7.4.1 TargetSite属性 250
7.4.2 StackTrace属性 251
7.4.3 HelpLink属性 251
7.4.4 Data属性 252
7.5 系统级异常(System.SystemException) 254
7.6 应用程序级异常(System.ApplicationException) 254
7.6.1 构建自定义异常,第一次尝试 255
7.6.2 构建自定义异常,第二次尝试 256
7.6.3 构建自定义异常,第三次尝试 257
7.7 处理多个异常 258
7.7.1 一般性的catch语句 260
7.7.2 重新抛出异常 260
7.7.3 内部异常 261
7.7.4 finally块 261
7.7.5 异常过滤器 262
7.8 使用Visual Studio调试未处理的异常 263
7.9 小结 264
第8章 使用接口 265
8.1 理解接口类型 265
8.2 定义自定义接口 268
8.3 实现接口 269
8.4 在对象级别调用接口成员 271
8.4.1 获取接口引用:as关键字 272
8.4.2 获取对象引用:is关键字(7.0更新) 272
8.5 默认实现(8.0新增) 273
8.6 静态构造函数和成员(8.0新增) 274
8.7 将接口用作参数 274
8.8 将接口用作返回值 276
8.9 接口类型的数组 276
8.10 使用Visual Studio或Visual Studio Code实现接口 278
8.11 显式接口实现 279
8.12 设计接口层次 281
8.12.1 具有默认实现的接口层次(8.0新增) 282
8.12.2 接口类型的多继承 283
8.13 IEnumerable和IEnumerator接口 285
8.13.1 使用yield关键字构建迭代器方法 287
8.13.2 构建命名迭代器 290
8.14 ICloneable接口 291
8.15 IComparable接口 295
8.15.1 使用IComparer指定多个排序顺序 298
8.15.2 自定义属性和自定义排序类型 299
8.16 小结 299
第9章 理解对象的生存期 301
9.1 类、对象和引用 301
9.2 对象生存期的基础知识 302
9.2.1 new的CIL 303
9.2.2 将对象引用设置为null 304
9.3 判断对象是否仍然存活 304
9.4 理解对象的“代” 305
9.5 垃圾回收类型 307
9.6 System.GC类型 307
9.7 构建可终结对象 311
9.7.1 重写System.Object.Finalize() 312
9.7.2 详细说明终结过程 313
9.8 构建可释放对象 314
9.8.1 重用C#的using关键字 315
9.8.2 using声明(8.0新增) 317
9.9 构建可终结、可释放的类型 317
9.10 理解对象的延迟实例化 320
9.11 小结 323
第IV部分 C#高级编程
第10章 集合和泛型 327
10.1 创建集合类的动机 327
10.1.1 System.Collections名称空间 328
10.1.2 System.Collections.Specialized名称空间简介 330
10.2 非泛型集合的问题 330
10.2.1 性能问题 331
10.2.2 类型安全性问题 334
10.2.3 初识泛型集合 337
10.3 泛型类型参数的作用 337
10.3.1 为泛型类/结构指定类型参数 338
10.3.2 为泛型成员指定类型参数 339
10.3.3 为泛型接口指定类型参数 340
10.4 System.Collections.Generic名称空间 341
10.4.1 理解集合初始化语法 342
10.4.2 使用List<T>类 343
10.4.3 使用Stack<T>类 345
10.4.4 使用Queue<T>类 346
10.4.5 使用PriorityQueue<TElement, TPriority>类(10新增) 347
10.4.6 使用SortedSet<T>类 347
10.4.7 使用Dictionary<TKey, TValue>类 349
10.5 System.Collections.ObjectModel名称空间 350
10.6 创建自定义泛型方法 352
10.7 创建自定义泛型结构和类 354
10.7.1 泛型的默认值表达式 355
10.7.2 default字面值表达式(7.1新增) 356
10.7.3 泛型的模式匹配(7.1新增) 357
10.8 约束类型参数 357
10.8.1 使用where关键字的示例 358
10.8.2 没有操作符约束 359
10.9 小结 360
第11章 C#的高级语言特性 361
11.1 理解索引器方法 361
10.1.1 使用字符串值索引数据 363
10.1.2 重载索引器方法 364
10.1.3 多维索引器 364
10.1.4 接口类型中的索引器定义 365
11.2 理解操作符重载 366
11.2.1 重载二元操作符 366
11.2.2 +=和-=操作符 368
11.2.3 重载一元操作符 369
11.2.4 重载相等性操作符 369
11.2.5 重载比较操作符 370
11.2.6 关于操作符重载的最后一点说明 371
11.3 理解自定义类型转换 371
11.3.1 回顾:数值转换 371
11.3.2 回顾:相关类类型之间的转换 371
11.3.3 创建自定义转换例程 372
11.3.4 Square类型的其他显式转换 375
11.3.5 定义隐式转换例程 375
11.4 理解扩展方法 376
11.4.1 定义扩展方法 377
11.4.2 调用扩展方法 378
11.4.3 导入扩展方法 378
11.4.4 扩展实现了特定接口的类型 379
11.4.5 扩展方法GetEnumerator支持foreach循环(9.0新增) 380
11.5 理解匿名类型 381
11.5.1 定义匿名类型 382
11.5.2 匿名类型的内部表示 382
11.5.3 ToString()和GetHashCode()的实现 384
11.5.4 匿名类型的相等性语义 384
11.5.5 在匿名类型中包含匿名类型 386
11.6 使用指针类型 386
11.6.1 unsafe关键字 388
11.6.2 使用*和&操作符 389
11.6.3 非安全的(和安全的)交换函数 390
11.6.4 通过指针访问字段(->操作符) 391
11.6.5 stackalloc关键字 392
11.6.6 通过fixed关键字钉住类型 392
11.6.7 sizeof关键字 393
11.7 小结 393
第12章 委托、事件和lambda表达式 395
12.1 理解委托类型 395
12.1.1 在C#中定义委托类型 396
12.1.2 System.MulticastDelegate和System.Delegate基类 398
12.2 最简单的委托示例 399
12.3 使用委托发送对象状态通知 401
12.3.1 启用多播 404
12.3.2 从委托的调用列表中移除目标 405
12.3.3 方法组转换语法 406
12.4 理解泛型委托 407
12.5 理解C#事件 410
12.5.1 C#的event关键字 411
12.5.2 事件的底层机制 413
12.5.3 监听传入的事件 413
12.5.4 使用Visual Studio简化事件注册 415
12.5.5 创建自定义事件实参 416
12.5.6 EventHandler<T>泛型委托 417
12.6 理解C#的匿名方法 418
12.6.1 访问局部变量 419
12.6.2 对匿名方法使用static (9.0新增) 420
12.6.3 在匿名方法中使用丢弃操作符(9.0新增) 421
12.7 理解lambda表达式 421
12.7.1 分解lambda表达式 423
12.7.2 在多条语句中处理实参 424
12.7.3 具有多个(或零个)参数的lambda表达式 425
12.7.4 在lambda表达式中使用static关键字(9.0新增) 426
12.7.5 在lambda表达式中使用丢弃操作符(9.0新增) 427
12.7.6 使用lambda表达式修改CarEvents示例 427
12.7.7 lambda和表达式体成员(7.0更新) 428
12.8 小结 428
第13章 LINQ to Objects 431
13.1 特定于LINQ的编程结构 431
13.1.1 隐式确定局部变量的类型 432
13.1.2 对象和集合初始化语法 432
13.1.3 lambda表达式 433
13.1.4 扩展方法 433
13.1.5 匿名类型 434
13.2 理解LINQ的作用 434
13.2.1 LINQ表达式是强类型的 435
13.2.2 核心的LINQ程序集 435
13.3 对原始数组应用LINQ查询 435
13.3.1 使用扩展方法的版本 437
13.3.2 不使用LINQ的版本 437
13.3.3 LINQ结果集的反射 438
13.3.4 LINQ和隐式确定类型的局部变量 439
13.3.5 LINQ和扩展方法 441
13.3.6 延迟执行的作用 441
13.3.7 立即执行的作用 443
13.4 返回LINQ查询的结果 445
13.5 对集合对象应用LINQ查询 446
13.5.1 访问被包含的子对象 447
13.5.2 对非泛型集合应用LINQ查询 448
13.5.3 使用OfType<T>()过滤数据 448
13.6 探索C#的LINQ查询操作符 449
13.6.1 基本选择语法 450
13.6.2 获取数据子集 451
13.6.3 分页数据 451
13.6.4 使用范围分页数据(10.0新增) 453
13.6.5 使用块分页数据(10.0新增) 453
13.6.6 投影新的数据类型 454
13.6.7 投影到不同的数据类型 455
13.6.8 使用Enumerable获取计数 456
13.6.9 获取未枚举的计数(10.0新增) 456
13.6.10 翻转结果集 457
13.6.11 排序表达式 457
13.6.12 将LINQ用作更好的韦恩图工具 458
13.6.13 删除重复项 460
13.6.14 LINQ聚合操作 461
13.7 LINQ查询表达式的内部表示 462
13.7.1 使用查询操作符构建查询表达式(回顾) 463
13.7.2 使用Enumerable类型和lambda表达式构建查询表达式 463
13.7.3 使用Enumerable类型和匿名方法构建查询表达式 464
13.7.4 使用Enumerable类型和原始委托构建查询表达式 465
13.8 小结 466
第14章 进程、应用程序域和加载上下文 467
14.1 Windows进程的作用 467
14.2 使用.NET Core与进程交互 469
14.2.1 枚举运行中的进程 470
14.2.2 调查具体进程 471
14.2.3 调查进程的线程集合 471
14.2.4 调查进程的模块集合 473
14.2.5 在代码中启动和终止进程 474
14.2.6 使用ProcessStartInfo类控制进程的启动过程 475
14.2.7 通过ProcessStartInfo使用OS动词 476
14.3 理解.NET应用程序域 477
14.3.1 System.AppDomain类 478
14.3.2 与默认应用程序域交互 478
14.3.3 枚举已加载的程序集 479
14.4 使用应用程序加载上下文隔离程序集 480
14.5 总结进程、应用程序域和加载上下文 482
14.6 小结 482
第15章 多线程、并行编程和异步编程 483
15.1 进程/AppDomain/上下文/线程的关系 483
15.1.1 并发问题 484
15.1.2 线程同步的作用 484
15.2 System.Threading 名称空间 485
15.3 System.Threading.Thread类 485
15.3.1 获取当前执行线程的统计信息 486
15.3.2 Name属性 487
15.3.3 Priority属性 487
15.4 手动创建辅助线程 488
15.4.1 使用ThreadStart委托 488
15.4.2 使用 ParameterizedThreadStart 委托 490
15.4.3 AutoResetEvent 类 491
15.4.4 前台线程和后台线程 491
15.5 并发性问题 492
15.5.1 使用C#的lock关键字进行同步 494
15.5.2 使用System.Threading.Monitor类型进行同步 496
15.5.3 使用System.Threading.Interlocked类型进行同步 497
15.6 使用定时器回调进行编程 497
15.7 理解ThreadPool 499
15.8 使用任务并行库进行并行编程 500
15.8.1 System.Threading.Tasks名称空间 501
15.8.2 Parallel类的作用 501
15.8.3 使用Parallel类的数据并行性 501
15.8.4 在辅助线程上访问UI元素 505
15.8.5 Task类 505
15.8.6 处理取消请求 506
15.8.7 使用 Parallel 类实现任务并行性 507
15.9 并行LINQ查询(PLINQ) 510
15.9.1 选择使用PLINQ查询 511
15.9.2 取消PLINQ查询 511
15.10 使用async/await模式的异步调用 512
15.10.1 C#关键字async和await (7.1、9.0更新) 513
15.10.2 SynchronizationContext和async/await 514
15.10.3 ConfigureAwait的作用 514
15.10.4 异步方法的命名约定 515
15.10.5 不返回数据的异步方法 515
15.10.6 有多个await的async方法 518
15.10.7 从同步方法中调用异步方法 519
15.10.8 在catch和finally块中使用await关键字 521
15.10.9 泛化的异步返回类型(7.0新增) 521
15.10.10 局部函数使用async/await(7.0新增) 521
15.10.11 取消async/await操作 522
15.10.12 异步流(8.0新增) 525
15.10.13 Parallel.ForEachAsync()方法(10.0新增) 526
15.10.14 使用async/await更新Book Reader应用程序 527
15.10.15 async和await总结 527
15.11 小结 528
第V部分 .NET Core程序集编程
第16章 构建和配置类库 531
16.1 定义自定义名称空间(10.0更新) 531
16.1.1 使用完全限定名称解决名称冲突 533
16.1.2 使用别名解决名称冲突 533
16.1.3 创建嵌套的名称空间 534
16.1.4 使用Visual Studio 2022更改根名称空间 535
16.1.5 使用项目文件更改根名称空间 535
16.2 .NET程序集的作用 536
16.2.1 程序集促进了代码重用 536
16.2.2 程序集确定了类型边界 536
16.2.3 程序集是可版本化的单元 537
16.2.4 程序集具有自我描述性 537
16.3 理解.NET程序集的格式 537
16.3.1 安装C++性能分析工具 537
16.3.2 操作系统(Windows)文件头 538
16.3.3 CLR文件头 538
16.3.4 CIL代码、类型元数据和程序集清单 539
16.3.5 可选的程序集资源 539
16.4 类库与控制台应用程序 540
16.5 .NET Standard与.NET (Core)类库 540
16.6 使用配置文件配置应用程序 541
16.6.1 多个配置文件 542
16.6.2 使用对象(10.0更新) 543
16.6.3 其他配置选项 545
16.7 构建和使用.NET类库 546
16.7.1 探索清单 548
16.7.2 探索CIL 549
16.7.3 探索类型元数据 550
16.7.4 构建C#客户端应用程序 551
16.7.5 构建Visual Basic客户端应用程序 552
16.7.6 跨语言继承的应用 553
16.7.7 将内部类型暴露给其他程序集 553
16.8 NuGet和.NET Core 554
16.8.1 使用NuGet打包程序集 554
16.8.2 引用NuGet包 555
16.9 发布控制台应用程序(.NET 5/6更新) 556
16.9.1 发布框架依赖型应用程序 557
16.9.2 发布自包含型应用程序 557
16.10 .NET如何定位程序集 559
16.11 小结 560
第17章 类型反射、延迟绑定、特性与动态类型 561
17.1 类型元数据的必要性 561
17.1.1 查看EngineStateEnum枚举的(部分)元数据 562
17.1.2 查看Car类型的(部分)元数据 563
17.1.3 TypeRef 564
17.1.4 记录定义的程序集 564
17.1.5 记录引用的程序集 565
17.1.6 记录字符串字面值 565
17.2 理解反射 566
17.2.1 System.Type类 566
17.2.2 使用System.Object.GetType()获取类型引用 567
17.2.3 使用typeof()获取类型引用 567
17.2.4 使用System.Type.GetType()获取类型引用 568
17.3 构建自定义的元数据查看器 568
17.3.1 反射方法 568
17.3.2 反射字段和属性 569
17.3.3 反射实现的接口 570
17.3.4 显示各种杂项 570
17.3.5 添加顶级语句 571
17.3.6 反射静态类型 572
17.3.7 反射泛型类型 572
17.3.8 反射方法参数和返回值 573
17.4 动态加载程序集 574
17.5 反射框架程序集 575
17.6 理解延迟绑定 577
17.6.1 System.Activator类 577
17.6.2 调用无参数的方法 578
17.6.3 调用带参数的方法 579
17.7 理解.NET特性的作用 580
17.7.1 特性消费者 580
17.7.2 在C#中应用特性 581
17.7.3 C#特性简写表示法 581
17.7.4 为特性指定构造函数参数 582
17.7.5 Obsolete特性的实际应用 582
17.8 构建自定义特性 583
17.8.1 应用自定义特性 584
17.8.2 命名属性语法 584
17.8.3 限制特性的使用 585
17.9 程序集级别的特性 585
17.10 在项目文件中添加程序集特性 586
17.11 使用提前绑定反射特性 587
17.12 使用延迟绑定反射特性 588
17.13 反射、延迟绑定和自定义特性的实际应用 589
17.14 构建可扩展的应用程序 590
17.14.1 构建多项目的ExtendableApp解决方案 590
17.14.2 构建CommonSnappableTypes.dll 593
17.14.3 构建C#插件 593
17.14.4 构建Visual Basic插件 594
17.14.5 为ExtendableApp添加代码 594
17.15 C#中dynamic关键字的作用 596
17.15.1 调用动态声明的数据的成员 598
17.15.2 dynamic关键字的应用范围 599
17.15.3 dynamic关键字的限制 600
17.15.4 dynamic关键字的实际应用 600
17.16 动态语言运行时的作用 600
17.16.1 表达式树的作用 601
17.16.2 表达式树的动态运行时查找 601
17.17 使用动态类型简化延迟绑定调用 602
17.18 小结 604
第18章 理解CIL和动态程序集的作用 607
18.1 学习CIL语法的动机 607
18.2 探究CIL指令、属性和操作码 608
18.2.1 CIL指令的作用 608
18.2.2 CIL属性的作用 609
18.2.3 CIL操作码的作用 609
18.2.4 CIL操作码/CIL助记符的区别 609
18.3 入栈与出栈:CIL的基于栈的本质 610
18.4 理解往返工程 611
18.4.1 CIL代码标签的作用 613
18.4.2 与CIL交互:修改*.il文件 614
18.4.3 使用ILASM.EXE编译CIL代码 614
18.4.4 使用Microsoft.NET.Sdk.il项目编译CIL代码 615
18.5 理解CIL指令和属性 616
18.5.1 在CIL中指定外部引用的程序集 616
18.5.2 在CIL中定义当前程序集 617
18.5.3 在CIL中定义名称空间 617
18.5.4 在CIL中定义类类型 618
18.5.5 在CIL中定义和实现接口 619
18.5.6 在CIL中定义结构 619
18.5.7 在CIL中定义枚举 620
18.5.8 在CIL中定义泛型 620
18.5.9 编译CILTypes.il文件 621
18.6 .NET基类库、C#和CIL数据类型映射 621
18.7 在CIL中定义类型成员 622
18.7.1 在CIL中定义字段数据 622
18.7.2 在CIL中定义类型构造函数 622
18.7.3 在CIL中定义属性 623
18.7.4 定义成员参数 623
18.8 深入探讨CIL操作码 624
18.8.1 .maxstack指令 626
18.8.2 在CIL中声明局部变量 626
18.8.3 在CIL中将参数映射到局部变量 627
18.8.4 隐藏的this引用 627
18.8.5 在CIL中表示迭代结构 628
18.8.6 CIL小结 629
18.9 理解动态程序集 629
18.9.1 探索System.Reflection.Emit名称空间 629
18.9.2 System.Reflection.Emit.ILGenerator的作用 630
18.9.3 发出动态程序集 631
18.9.4 发出程序集和模块集 633
18.9.5 ModuleBuilder类型的作用 633
18.9.6 发出HelloWorld类类型和字符串成员变量 634
18.9.7 发出构造函数 634
18.9.8 发出SayHello()方法 635
18.9.9 使用动态生成的程序集 635
18.10 小结 636
第VI部分 文件处理、对象序列化和数据访问
第19章 文件I/O和对象序列化 639
19.1 探索System.IO名称空间 639
19.2 Directory(Info)和File(Info)类型 640
19.3 使用DirectoryInfo类型 641
19.3.1 使用DirectoryInfo类型枚举文件 642
19.3.2 使用DirectoryInfo类型创建子目录 643
19.4 使用Directory类型 644
19.5 使用DriveInfo类型 644
19.6 使用FileInfo类 645
19.6.1 FileInfo.Create()方法 646
19.6.2 FileInfo.Open()方法 647
19.6.3 FileInfo.OpenRead()和FileInfo.OpenWrite()方法 648
19.6.4 FileInfo.OpenText()方法 649
19.6.5 FileInfo.CreateText()和FileInfo.AppendText()方法 649
19.7 使用File类型 649
19.8 Stream抽象类 651
19.9 使用StreamWriter和StreamReader 653
19.9.1 写入文本文件 654
19.9.2 读取文本文件 654
19.9.3 直接创建StreamWriter/StreamReader类型 655
19.10 使用StringWriter和StringReader 656
19.11 使用BinaryWriter和BinaryReader 657
19.12 在代码中监视文件 658
19.13 理解对象序列化 660
19.13.1 对象图的作用 660
19.13.2 创建示例类型和顶级语句 661
19.13.3 可扩展标记语言(XML) 663
19.13.4 JavaScript Object Notation (JSON)序列化 667
19.14 小结 677
第20章 使用ADO.NET访问数据 679
20.1 ADO.NET与ADO 679
20.2 理解ADO.NET数据提供程序 679
20.3 System.Data名称空间中的类型 681
20.3.1 IDbConnection接口的作用 682
20.3.2 IDbTransaction接口的作用 682
20.3.3 IDbCommand接口的作用 683
20.3.4 IDbDataParameter和IDataParameter接口的作用 683
20.3.5 IDbDataAdapater和IDataAdapter接口的作用 684
20.3.6 IDataReader和IDataRecord接口的作用 684
20.4 使用接口抽象数据提供程序 685
20.5 设置SQL Server和Azure Data Studio 687
20.5.1 安装SQL Server 687
20.5.2 安装SQL Server IDE 689
20.5.3 连接SQL Server 689
20.6 还原AutoLot数据库备份 691
20.6.1 将备份文件复制到容器中 691
20.6.2 使用SSMS还原数据库 692
20.6.3 使用Azure Data Studio还原数据库 693
20.7 创建AutoLot数据库 694
20.7.1 创建数据库 694
20.7.2 创建表 694
20.7.3 创建表关系 696
20.7.4 创建GetPetName()存储过程 697
20.7.5 添加测试记录 698
20.8 ADO.NET数据提供程序工厂模型 699
20.8.1 一个完整的数据提供程序工厂示例 700
20.8.2 数据提供程序工厂模型的一个潜在缺陷 703
20.9 深入了解连接、命令和数据读取器 704
20.9.1 使用连接对象 705
20.9.2 使用Command对象 707
20.9.3 使用数据读取器 708
20.10 使用创建、更新和删除查询 709
20.10.1 创建Car和CarViewModel类 710
20.10.2 添加InventoryDal类 711
20.10.3 添加删除逻辑 714
20.10.4 添加更新逻辑 715
20.10.5 使用参数化的命令对象 715
20.10.6 执行存储过程 718
20.11 创建一个基于控制台的客户端应用程序 720
20.12 理解数据库事务 720
20.13 在InventoryDal中添加一个事务方法 722
20.14 使用ADO.NET执行批量复制 724
20.14.1 探索SqlBulkCopy类 724
20.14.2 创建自定义数据读取器 725
20.14.3 执行批量复制 728
20.14.4 测试批量复制 729
20.15 小结 729
第VII部分 Entity Framework Core
第21章 Entity Framework Core简介 733
21.1 对象关系映射器 734
21.2 理解Entity Framework Core的作用 734
21.3 Entity Framework的组成模块 735
21.3.1 DbContext类 735
21.3.2 DbSet<T>类 740
21.3.3 ChangeTracker 741
21.3.4 实体 742
21.3.5 嵌入的实体类型 768
21.3.6 查询类型 771
21.4 查询的执行 773
21.5 跟踪与非跟踪查询 774
21.6 对比代码优先与数据库优先 775
21.7 EF Core全局工具的CLI命令 775
21.7.1 migrations命令 777
21.7.2 数据库命令 780
21.7.3 DbContext命令 780
21.8 小结 782
第22章 探索Entity Framework Core 783
22.1 创建记录 783
22.1.1 实体状态 783
22.1.2 使用Add添加一条记录 784
22.1.3 使用Attach添加一条记录 785
22.1.4 一次添加多条记录 785
22.1.5 添加记录时关于identity列的考虑事项 786
22.1.6 添加对象图 788
22.1.7 添加多对多记录 789
22.1.8 添加样本记录 790
22.2 清除样本数据 791
22.3 查询数据 791
22.3.1 获取全部记录 792
22.3.2 过滤记录 792
22.3.3 排序记录 793
22.3.4 分页 795
22.3.5 获取单条记录 796
22.3.6 聚合方法 801
22.3.7 Any()和All() 802
22.3.8 从存储过程获取数据 803
22.4 查询关联数据 804
22.4.1 提前加载 804
22.4.2 显式加载 808
22.4.3 延迟加载 809
22.5 更新记录 812
22.5.1 实体状态 812
22.5.2 更新跟踪实体 812
22.5.3 更新未跟踪实体 813
22.6 删除记录 814
22.6.1 实体状态 814
22.6.2 删除跟踪记录 814
22.6.3 删除非跟踪实体 815
22.6.4 捕获失败的级联删除 815
22.7 值得关注的EF Core特性 815
22.7.1 全局查询过滤器 816
22.7.2 在LINQ中使用原生SQL查询 818
22.7.3 投影 820
22.7.4 处理数据库生成的值 821
22.7.5 并发性检查 822
22.7.6 连接的弹性 824
22.7.7 数据库函数的映射 825
22.7.8 EF.Functions 826
22.7.9 批量处理语句 828
22.7.10 值转换器 829
22.7.11 阴影属性 831
22.7.12 SQL Server对时态表的支持 833
22.8 小结 840
第23章 使用Entity Framework Core构建数据访问层 841
23.1 创建AutoLot.Dal和AutoLot.Models项目 841
23.2 添加数据库视图 842
23.3 搭建DbContext和实体 843
23.4 切换到代码优先 843
23.4.1 创建DbContext设计时工厂 844
23.4.2 创建初始迁移 844
23.4.3 应用迁移 845
23.5 创建GlobalUsings文件 845
23.6 创建自定义异常 846
23.7 完成实体和ViewModel 847
23.7.1 实体 847
23.7.2 视图模型 865
23.8 更新ApplicationDbContext 867
23.8.1 添加映射的数据库函数 868
23.8.2 处理DbContext和ChangeTracker事件 868
23.8.3 重写约定 869
23.8.4 重写SaveChanges方法 869
23.9 创建下一个迁移和更新数据库 870
23.10 使用EF迁移来创建/更新数据库对象 870
23.10.1 添加MigrationHelpers类 871
23.10.2 创建和更新迁移 872
23.10.3 应用迁移 873
23.11 添加存储库 873
23.11.1 添加IBaseViewRepo接口 873
23.11.2 添加BaseViewRepo实现 874
23.11.3 添加IBaseRepo接口 875
23.11.4 添加BaseRepo实现 876
23.11.5 添加ITemporalTableBaseRepo接口 878
23.11.6 添加TemporalTableBaseRepo实现 878
23.11.7 特定于实体的存储库接口 880
23.11.8 实现特定于实体的存储库 882
23.11.9 更新GlobalUsings.cs文件 887
23.12 在代码中处理数据库和迁移 887
23.13 数据初始化 889
23.13.1 创建样本数据 889
23.13.2 加载样本数据 891
23.14 小结 892
第24章 测试AutoLot 893
24.1 测试的准备工作 893
24.1.1 创建项目 893
24.1.2 使AutoLot.Dal的内部方法和类对AutoLot.Dal.Tests可见 894
24.1.3 添加GlobalUsings文件 894
24.1.4 初识xUnit 895
24.1.5 配置项目和DbContext实例 896
24.1.6 添加BaseTest类 897
24.1.7 添加EnsureAutoLotDatabase测试夹具类 899
24.1.8 添加集成测试类 899
24.2 查询数据库 902
24.2.1 LINQ查询 903
24.2.2 时态查询 920
24.2.3 带LINQ的SQL查询 921
24.2.4 聚合方法 922
24.2.5 Any()和All() 923
24.2.6 从存储过程获取数据 924
24.3 创建记录 924
24.3.1 添加单条记录 925
24.3.2 使用Attach添加单条记录 925
24.3.3 一次性添加多条记录 926
24.3.4 添加对象图 927
24.4 更新记录 928
24.4.1 更新跟踪实体 928
24.4.2 更新非跟踪实体 929
24.4.3 更新记录时的并发性检查 930
24.5 删除记录 930
24.5.1 删除跟踪记录 931
24.5.2 删除非跟踪实体 931
24.5.3 捕获级联删除失败 932
24.5.4 删除记录时的并发性检查 932
24.6 小结 932
第VIII部分 Windows客户端开发
第25章 Windows Presentation Foundation和XAML简介 935
25.1 WPF背后的动机 935
25.1.1 统一不同的API 936
25.1.2 通过XAML提供关注点隔离 936
25.1.3 提供优化的渲染模型 937
25.1.4 简化复杂的UI编程 937
25.2 探索WPF程序集 938
25.2.1 Application类的作用 939
25.2.2 构造Application类 939
25.2.3 枚举Windows集合 940
25.2.4 Window类的作用 940
25.3 理解WPF XAML语法 943
25.3.1 Kaxaml简介 943
25.3.2 XAML的XML名称空间和XAML“关键字” 945
25.3.3 控制类和成员变量的可见性 947
25.3.4 XAML元素、XAML属性和类型转换器 947
25.3.5 理解XAML的属性元素语法 948
25.3.6 理解XAML附加属性 949
25.3.7 理解XAML标记扩展 949
25.4 使用Visual Studio构建WPF应用程序 951
25.4.1 WPF项目模板 952
25.4.2 工具箱和XAML设计器/编辑器 952
25.4.3 使用Properties窗口设置属性 953
25.4.4 使用Properties窗口处理事件 954
25.4.5 在XAML编辑器中处理事件 955
25.4.6 Document Outline窗口 956
25.4.7 启用或禁用XAML调试器 956
25.4.8 检查App.xaml文件 957
25.4.9 将Window的XAML标记映射到C#代码 958
25.4.10 BAML的作用 960
25.4.11 解决Main()的谜题 960
25.4.12 与应用程序级数据交互 961
25.4.13 处理Window对象的关闭 962
25.4.14 拦截鼠标事件 962
25.4.15 拦截键盘事件 963
25.5 小结 964
第26章 WPF控件、布局、事件和数据绑定 965
26.1 WPF的核心控件 965
26.1.1 WPF的Ink控件 966
26.1.2 WPF的文档控件 966
26.1.3 WPF中的常用对话框 966
26.2 Visual Studio的WPF设计器概述 966
26.2.1 在Visual Studio中使用WPF控件 967
26.2.2 使用Document Outline编辑器 967
26.3 使用面板控制内容布局 968
26.3.1 在Canvas面板中定位内容 969
26.3.2 在WrapPanel面板中定位内容 970
26.3.3 在StackPanel面板中定位内容 972
26.3.4 在Grid面板中定位内容 973
26.3.5 使用GridSplitter类型的Grid 974
26.3.6 在DockPanel面板中定位内容 975
26.3.7 为面板类型启用滚动 976
26.3.8 使用Visual Studio设计器配置面板 976
26.4 使用嵌套面板构建窗口的框架 979
26.4.1 构建菜单系统 980
26.4.2 以可视化方式构建菜单 981
26.4.3 构建工具栏 981
26.4.4 构建状态栏 982
26.4.5 完成UI设计 982
26.4.6 实现MouseEnter/MouseLeave事件处理程序 983
26.4.7 实现拼写检查逻辑 983
26.5 理解WPF命令 984
26.5.1 内置的Command对象 984
26.5.2 将命令连接到Command属性 985
26.5.3 将命令连接到任意操作 986
26.5.4 使用Open和Save命令 987
26.6 理解路由事件 988
26.6.1 路由冒泡事件的作用 989
26.6.2 继续或停止冒泡 990
26.6.3 路由隧道事件的作用 990
26.7 深入介绍WPF的API和控件 992
26.8 构建Ink API选项卡 993
26.8.1 设计工具栏 993
26.8.2 RadioButton控件 994
26.8.3 添加Save、Load和Delete按钮 994
26.8.4 添加InkCanvas控件 994
26.8.5 预览窗口 995
26.8.6 处理Ink API选项卡的事件 995
26.8.7 在Toolbox中添加控件 995
26.8.8 InkCanvas控件 996
26.8.9 ComboBox控件 998
26.8.10 保存、加载和清空InkCanvas数据 999
26.9 WPF的数据绑定模型简介 1000
26.9.1 构建Data Binding选项卡 1000
26.9.2 建立数据绑定 1001
26.9.3 DataContext属性 1001
26.9.4 格式化绑定的数据 1002
26.9.5 使用IValueConverter进行数据转换 1002
26.9.6 在代码中建立数据绑定 1003
26.9.7 构建DataGrid选项卡 1004
26.10 理解依赖属性的作用 1006
26.10.1 检查现有的依赖属性 1008
26.10.2 关于CLR属性封装器的重要说明 1010
26.11 构建自定义依赖属性 1010
26.11.1 添加一个数据验证例程 1013
26.11.2 响应属性变化 1013
26.12 小结 1014
第27章 WPF图形渲染服务 1015
27.1 理解WPF的图形渲染服务 1015
27.2 使用形状渲染图形数据 1016
27.2.1 在画布上添加矩形、椭圆形和线条 1017
27.2.2 从画布中删除矩形、椭圆形和线条 1020
27.2.3 使用折线和多边形 1020
27.2.4 使用路径 1021
27.3 WPF的画刷和钢笔 1024
27.3.1 使用Visual Studio配置画刷 1024
27.3.2 在代码中配置画刷 1026
27.3.3 配置钢笔 1027
27.4 应用图形变换 1027
27.4.1 初识变换 1028
27.4.2 变换画布数据 1029
27.5 使用Visual Studio的变换编辑器 1030
27.5.1 构建初始布局 1030
27.5.2 在设计时应用变换 1032
27.5.3 在代码中变换画布 1032
27.6 使用绘图和几何图形渲染图形数据 1033
27.6.1 使用几何图形构建DrawingBrush 1034
27.6.2 使用DrawingBrush进行填充 1034
27.6.3 在DrawingImage中包含绘图类型 1035
27.7 使用矢量图形 1035
27.7.1 将示例矢量图形文件转换为XAML 1036
27.7.2 将图形数据导入WPF项目中 1036
27.7.3 与符号交互 1037
27.8 使用可视化层渲染图形数据 1037
27.8.1 Visual基类及其派生类 1038
27.8.2 DrawingVisual类的简单用法 1038
27.8.3 将可视化数据渲染到自定义布局管理器 1039
27.8.4 响应命中测试操作 1041
27.9 小结 1042
第28章 WPF资源、动画、样式和模板 1043
28.1 理解WPF的资源系统 1043
28.2 使用对象(逻辑)资源 1047
28.2.1 Resources属性的作用 1047
28.2.2 定义窗口范围的资源 1047
28.2.3 {StaticResource}标记扩展 1049
28.2.4 {DynamicResource}标记扩展 1050
28.2.5 应用程序级别的资源 1050
28.2.6 定义合并资源字典 1051
28.2.7 定义一个只包含资源的程序集 1051
28.3 理解WPF的动画服务 1052
28.3.1 Animation类类型的作用 1053
28.3.2 To、From和By属性 1053
28.3.3 Timeline基类的作用 1054
28.3.4 在C#代码中编写动画 1054
28.3.5 控制动画的节奏 1055
28.3.6 翻转和循环动画 1056
28.4 使用XAML编写动画 1057
28.4.1 故事板的作用 1057
28.4.2 事件触发器的作用 1058
28.4.3 使用离散的关键帧的动画 1058
28.5 理解WPF样式的作用 1059
28.5.1 定义和应用样式 1060
28.5.2 覆盖样式设置 1060
28.5.3 TargetType对样式的影响 1060
28.5.4 继承现有样式 1062
28.5.5 定义带触发器的样式 1062
28.5.6 定义带多个触发器的样式 1063
28.5.7 动画样式 1063
28.5.8 在代码中指定样式 1064
28.6 逻辑树、可视化树和默认模板 1065
28.6.1 通过代码检查逻辑树 1065
28.6.2 通过代码检查可视化树 1067
28.6.3 通过代码检查控件的默认模板 1067
28.7 使用触发器框架构建控件模板 1070
28.7.1 模板作为资源 1071
28.7.2 使用触发器包含视觉提示 1071
28.7.3 {TemplateBinding}标记扩展的作用 1072
28.7.4 ContentPresenter的作用 1073
28.7.5 在样式中包含模板 1073
28.8 小结 1074
第29章 WPF通知、验证、命令和MVVM 1075
29.1 模型-视图-视图模型简介 1075
29.1.1 模型 1075
29.1.2 视图 1075
29.1.3 视图模型 1076
29.1.4 贫血模型和贫血视图模型 1076
29.2 WPF的绑定通知系统 1076
29.2.1 可观察的模型和集合 1077
29.2.2 添加绑定和数据 1078
29.2.3 在代码中修改车辆数据 1079
29.2.4 可观察模型 1079
29.2.5 可观察集合 1081
29.2.6 通知和可观察对象小结 1083
29.3 WPF的验证 1083
29.3.1 为验证示例更新文件 1083
29.3.2 Validation类 1083
29.3.3 验证选项 1084
29.3.4 在WPF中利用数据注解 1092
29.3.5 自定义ErrorTemplate 1094
29.3.6 验证小结 1095
29.4 创建自定义命令 1095
29.4.1 实现ICommand接口 1096
29.4.2 添加ChangeColorCommand 1096
29.4.3 创建CommandBase类 1098
29.4.4 添加AddCarCommand类 1098
29.4.5 RelayCommand 1100
29.4.6 命令小结 1101
29.5 将代码和数据迁移到视图模型 1102
29.5.1 移动MainWindow.xaml.cs的代码 1102
29.5.2 更新MainWindow的代码和标记 1103
29.5.3 更新控件的标记 1103
29.5.4 视图模型小结 1103
29.6 为MVVM更新AutoLot.Dal 1104
29.7 小结 1104
第IX部分 ASP.NET Core
第30章 ASP.NET Core简介 1107
30.1 快速回顾ASP.NET MVC 1107
30.1.1 MVC模式简介 1107
30.1.2 ASP.NET Core和MVC模式 1108
30.2 ASP.NET Core和.NET Core 1108
30.3 创建和配置解决方案和项目 1109
30.3.1 使用Visual Studio 2022 1109
30.3.2 使用命令行 1113
30.3.3 更新Entity Framework Core的包引用 1115
30.3.4 为所有项目禁用可空引用类型 1115
30.3.5 在每个项目中创建一个GlobalUsing.cs类 1115
30.4 运行ASP.NET Core应用程序 1115
30.4.1 使用Visual Studio 1116
30.4.2 使用Visual Studio Code 1117
30.4.3 使用命令行或Terminal窗口 1117
30.4.4 在调试时修改代码 1117
30.4.5 调试ASP.NET Core应用程序 1117
30.4.6 更新AutoLot.Api和AutoLot.Web Kestrel端口 1118
30.5 ASP.NET Core中来自MVC/Web API的概念 1119
30.5.1 约定先于配置 1119
30.5.2 控制器和动作(基于MVC的Web应用程序和RESTful服务) 1120
30.5.3 目录结构约定 1122
30.5.4 路由 1123
30.5.5 模型绑定 1132
30.5.6 模型验证 1137
30.5.7 过滤器 1139
30.6 小结 1140
第31章 深入介绍ASP.NET Core 1141
31.1 ASP.NET Core中的新增特性 1141
31.2 Razor页面 1141
31.2.1 Razor页面文件 1142
31.2.2 PageModel类 1142
31.2.3 页面处理程序方法 1143
31.3 环境感知 1143
31.4 WebAppBuilder和WebApp 1145
31.4.1 Restful服务的Program.cs文件 1146
31.4.2 MVC风格的应用程序中的Program.cs文件 1147
31.4.3 基于Razor页面的应用程序中的Program.cs文件 1148
31.5 应用程序配置 1148
31.6 内置依赖注入 1149
31.6.1 在依赖注入容器中添加对Web应用程序的支持 1150
31.6.2 将DbContext派生类添加到DI容器中 1151
31.6.3 在依赖注入容器中添加自定义服务 1151
31.6.4 依赖的层次结构 1153
31.6.5 注入依赖 1153
31.6.6 在Program.cs中获取依赖 1154
31.6.7 构建共享数据服务 1155
31.7 ASP.NET Core中的选项模式 1159
31.8 HTTP客户端工厂 1161
31.8.1 基本用法 1161
31.8.2 命名客户端 1162
31.8.3 类型化客户端 1162
31.8.4 AutoLot API服务封装器 1163
31.9 部署ASP.NET Core应用程序 1171
31.10 轻量级、模块化的HTTP请求管道 1171
31.11 日志记录 1171
31.11.1 使用Serilog添加日志记录 1173
31.11.2 AutoLot日志记录框架 1178
31.11.3 将日志记录添加到数据服务中 1182
31.11.4 测试日志记录框架 1183
31.12 字符串实用方法 1184
31.13 小结 1184
第32章 使用ASP.NET Core开发RESTful服务 1185
32.1 ASP.NET Core RESTful服务简介 1185
32.2 RESTful服务中的控制器动作 1185
32.2.1 格式化后的JSON响应结果 1186
32.2.2 ApiController特性 1189
32.3 API版本化 1193
32.3.1 微软的REST API指导原则 1193
32.3.2 添加版本化NuGet包 1193
32.3.3 添加API版本支持 1194
32.3.4 API版本特性 1197
32.3.5 版本交叉 1198
32.3.6 查询字符串版本的请求和路由 1199
32.3.7 获取请求中的API版本 1201
32.3.8 URL片段版本化的路由更新 1201
32.3.9 弃用版本 1202
32.3.10 对不支持版本的请求 1202
32.3.11 添加API版本资源管理器 1203
32.4 更新Swagger/OpenAPI设置 1204
32.4.1 添加XML文档文件 1204
32.4.2 应用程序的Swagger设置 1206
32.4.3 SwaggerDefaultValues操作过滤器 1209
32.4.4 ConfigureSwaggerOptions类 1210
32.4.5 更新SwaggerGen()调用 1211
32.4.6 更新UseSwaggerUI()调用 1212
32.4.7 在Swagger UI中查看结果 1212
32.4.8 API端点的其他文档选项 1214
32.5 构建BaseCrudController 1215
32.5.1 构造函数 1216
32.5.2 Get方法 1216
32.5.3 UpdateOne方法 1218
32.5.4 AddOne方法 1219
32.5.5 DeleteOne方法 1220
32.6 CarsController 1221
32.7 剩余的控制器 1222
32.8 异常过滤器 1224
32.8.1 创建自定义异常过滤器 1224
32.8.2 测试异常过滤器 1226
32.9 添加跨源请求支持 1226
32.9.1 创建CORS策略 1226
32.9.2 将CORS策略添加到HTTP管道处理中 1227
32.10 基本身份验证 1227
32.10.1 添加和配置安全性信息 1227
32.10.2 构建基本身份验证处理程序 1228
32.10.3 注册基本身份验证处理程序并保护控制器安全 1230
32.11 小结 1232
第33章 使用MVC开发Web应用程序 1233
33.1 ASP.NET Core中的“V” 1233
33.1.1 ViewResult和动作方法 1233
33.1.2 Razor视图引擎和Razor语法 1236
33.1.3 视图 1238
33.1.4 布局 1242
33.1.5 分部视图 1243
33.1.6 将布局拆分为分部视图 1244
33.1.7 将数据发送给视图 1245
33.2 管理客户端库 1248
33.2.1 将Library Manager作为.NET全局工具安装 1248
33.2.2 在AutoLot.Mvc中添加客户端库 1248
33.3 捆绑和最小化 1251
33.3.1 捆绑 1251
33.3.2 最小化 1251
33.3.3 WebOptimizer解决方案 1251
33.4 控制器 1253
33.4.1 HomeController 1254
33.4.2 BaseCrudController 1254
33.4.3 CarsController 1261
33.5 区域 1262
33.5.1 区域路由 1263
33.5.2 MakesController区域控制器 1263
33.5.3 _ViewImports和_ViewStart 1264
33.6 标签助手 1264
33.6.1 启用标签助手 1267
33.6.2 Form标签助手 1267
33.6.3 Form Action按钮/图像标签助手 1269
33.6.4 Anchor标签助手 1269
33.6.5 Input标签助手 1269
33.6.6 TextArea标签助手 1270
33.6.7 Select标签助手 1270
33.6.8 Validation标签助手 1271
33.6.9 Environment标签助手 1272
33.6.10 Link标签助手 1273
33.6.11 Script标签助手 1274
33.6.12 Image标签助手 1275
33.7 自定义标签助手 1276
33.7.1 搭建基础 1276
33.7.2 创建基类 1276
33.7.3 Item Details标签助手 1277
33.7.4 Item Delete标签助手 1278
33.7.5 Item Edit标签助手 1279
33.7.6 Item Create标签助手 1279
33.7.7 Item List标签助手 1280
33.7.8 使自定义标签助手可见 1280
33.8 HTML助手 1281
33.8.1 DisplayFor HTML助手 1281
33.8.2 DisplayForModel HTML助手 1281
33.8.3 EditorFor和EditorForModel HTML助手 1282
33.9 Car视图 1282
33.9.1 Car List分部视图 1282
33.9.2 Index视图 1283
33.9.3 ByMake视图 1284
33.9.4 Details视图 1285
33.9.5 Create视图 1285
33.9.6 Edit视图 1286
33.9.7 Delete视图 1288
33.10 视图组件 1289
33.10.1 服务器端代码 1289
33.10.2 构建分部视图 1290
33.10.3 调用视图组件 1290
33.10.4 更新菜单 1291
33.11 自定义验证特性 1291
33.11.1 搭建基础 1292
33.11.2 服务器端验证 1294
33.11.3 客户端验证 1298
33.11.4 更新Validation Scripts分部视图 1301
33.12 通用数据保护条例支持 1301
33.12.1 添加cookie政策支持 1301
33.12.2 用于支持cookie的分部视图 1302
33.12.3 用于同意/撤回使用cookie的菜单支持 1303
33.13 完成Admin区域 1304
33.14 同时运行AutoLot.Mvc和AutoLot.Api 1306
33.14.1 使用Visual Studio 1307
33.14.2 使用命令行 1307
33.15 小结 1308
第34章 使用Razor页面开发Web应用程序 1309
34.1 Razor页面的结构 1309
34.1.1 Razor页面的PageModel类和页面处理程序方法 1309
34.1.2 Razor页面视图 1311
34.1.3 Razor视图 1313
34.1.4 分部视图 1317
34.1.5 ViewBag、ViewData和TempData 1318
34.2 向AutoLot.Web添加客户端库 1319
34.2.1 添加libman.json文件 1319
34.2.2 更新libman.json文件 1319
34.2.3 更新JavaScript和CSS引用 1321
34.3 添加并配置WebOptimizer 1322
34.4 标签助手 1323
34.4.1 启用标签助手 1324
34.4.2 Form标签助手 1324
34.4.3 Form Action按钮/图像标签助手 1325
34.4.4 Anchor标签助手 1325
34.5 自定义标签助手 1325
34.5.1 更新Program.cs 1325
34.5.2 创建基类 1326
34.5.3 Item Details标签助手 1327
34.5.4 Item Delete标签助手 1328
34.5.5 Item Edit标签助手 1328
34.5.6 Item Create标签助手 1329
34.5.7 Item List标签助手 1329
34.5.8 使自定义标签助手可见 1330
34.6 Cars Razor页面 1330
34.6.1 BasePageModel类 1330
34.6.2 Index Razor页面 1333
34.6.3 Details Razor页面 1336
34.6.4 Create Razor页面 1337
34.6.5 Edit Razor页面 1338
34.6.6 Delete Razor页面 1340
34.7 视图组件 1342
34.8 区域 1343
34.8.1 Razor页面的区域路由 1343
34.8.2 _ViewImports和_ViewStart 1343
34.8.3 Makes Razor页面 1343
34.8.4 添加区域的菜单项 1348
34.9 自定义验证特性 1349
34.10 通用数据保护条例支持 1352
34.10.1 用于支持cookie的分部视图 1352
34.10.2 用于同意/撤回使用cookie的菜单支持 1353
34.11 小结 1354
