简介:这是Packt出版社《C#10和.NET6——现代跨平台开发》第六版的配套源码仓库,书籍预计2021年11月出版,定位为中级C#开发者进阶实践材料,适合正在阅读该书的读者边读边练,也适合希望了解现代跨平台项目结构的团队开发者参考。压缩包共229个文件,整体大小约18.36MB,以108个cs源文件、70个csproj工程文件为主,辅以sln解决方案、工作区配置,以及ipynb笔记、md说明、jpg图例和sql脚本,目录按第1至第6章逐章组织,便于直接打开对应工程,覆盖从基础语法到完整项目实践的各个阶段。已有494人学习下载。读者可获得各章示例工程与关键案例的完整实现代码,还有可复现的开发环境配置,能对照书籍逐项运行验证。整套资料可帮助系统掌握C#10新特性与.NET6跨平台开发流程。
1. cs10dotnet6 是什么仓库:它不是工具库,而是教程的配套源码
第一次打开 cs10dotnet6 仓库,很多人会以为它是一个 C# 工具库或框架源码,实际不是。它是某出版社一本以现代跨平台开发为主线、讲 C# 10 与 .NET 6 的开发书的配套示例仓库,作者把书中每一章的代码整理成独立项目放进来,覆盖从控制台程序、LINQ、EF Core 数据访问到 ASP.NET Core Web API 的整条链路。仓库本身不解决业务问题,但它是你跟着书走到底时最可靠的对照物:印刷出来的代码片段经常为了排版做删减,仓库里的才是能编译、能运行的完整工程。适合三类人:零基础跟着书学 C# 的新手、想快速验证某个 .NET 6 特性是否如文档所述的熟手、要给团队做内部培训而需要成体系代码素材的工程师。下面按我把这个仓库用起来并踩过坑的顺序来写。
2. 动手前先对齐环境:.NET 6 SDK、global.json 与仓库结构
2.1 检查 SDK 版本与克隆方式
仓库里的项目目标框架基本都是 net6.0。理论上,本机只装新版 SDK(比如 8.0.x)也能编译 net6.0 目标,因为目标框架包会从 NuGet 拉取,但书中的命令输出、项目模板结构都是按 .NET 6 SDK 写的,SDK 版本不一致时,你会遇到“命令一样、生成的东西不一样”的困惑。我一般的做法是先把主版本对齐:
# 查看本机安装的 SDK 列表 dotnet --list-sdks看输出里有没有 6.0.x 这一行。如果只有 7.0 或 8.0,建议先补装 .NET 6 SDK 再继续。SDK 和运行时是两回事,--list-sdks只列 SDK;同一台机器可以同时装多个 SDK,互不冲突。
判断仓库具体锁定哪个 SDK,看根目录有没有 global.json。这个文件长这样:
{ "sdk": { "version": "6.0.100", "rollForward": "latestFeature" } }如果你的 SDK 不在 global.json 允许的范围内,任何 dotnet 命令都会报“找不到指定 SDK”然后退出。两条路:按文件版本补装 SDK,或者临时把这个文件改名,让系统用现有 SDK 编译。我建议优先选前者,因为书里的行为和 6.0 的编译器表现是对应的,换成新版 SDK 后,同样的代码可能给出不同的警告集。
克隆仓库不需要特殊姿势,浅克隆就够了:
git clone --depth 1 <仓库地址> cs10dotnet6 cd cs10dotnet6 lsclone 下来先花两分钟看目录再动手:常见结构是每章一个一级目录,命名带 Chapter_01 这类前缀,里面再放解决方案和项目,具体名字以你下载到的实际内容为准。我不建议用 sparse-checkout 只拉单章,因为后面的章节项目会引用前面章节的类库,只拉一章大概率连 restore 都过不了。
2.2 认识 .NET 6 模板:ImplicitUsings 与 Nullable 的差异
书里第一章教你创建控制台项目,.NET 6 的模板和早期 .NET Framework 时代差别很大。仓库里最典型的 csproj 长这样:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>对应的 Program.cs 可能只有一行:
Console.WriteLine("Hello, World!");这里有个容易忽略的点:Program.cs 里没有 using System,却能直接用 Console,是因为 ImplicitUsings 开启后,编译器会隐式引入 System、System.Linq、System.Collections.Generic 等常用命名空间。Nullable 开启后,可空引用类型检查会全程生效,书里的练习经常利用这一点让你理解引用类型为空的场景。这两个开关和你公司项目里的写法不一致时,同一个文件编译出来的警告集会完全不同。如果你用旧模板新建项目,没有这两个开关,照抄仓库代码时会出现“明明一样却多了警告或少了行为”的奇怪体验。
跑第一个示例用 dotnet run:
# 进入某一章目录后运行指定项目(项目名以实际为准) dotnet run --project Chapter_01/Chapter01.csprojrun 会先隐式 restore 再 build,新环境第一次跑会比较慢,等它把依赖拉完。如果你在公司内网,还原失败时先查 NuGet 源:
dotnet nuget list source确认源地址是官方源还是内部镜像,再决定是换源还是走内网代理。这一步能排除掉一大半还原报错的假象。
提示:不要为了图省事直接把 global.json 删掉再提交到自己的仓库分支。这个文件是作者用来锁定编译行为的,删了虽然能编过,但你就失去了和书上行为对齐的基准。
2.3 解决方案、项目与类库的引用关系
仓库里多数章节不是单个控制台项目,而是一个解决方案包含两三个项目:一个类库放模型和上下文,一个控制台做演示,偶尔还有测试项目。理解这个结构,你才能解释“为什么 dotnet run 有时要指定项目”。
# 查看解决方案里包含哪些项目 dotnet sln Chapter_03.sln list输出会列出项目路径和类型。类库项目输出的是 dll,控制台项目才是程序入口。所以运行某个示例时,入口通常是名字带 Console、App、Demo 的那个项目,而不是类库。如果你对一个没有设置 OutputType=Exe 的项目执行 run,dotnet 会直接报“不是可执行项目”。
遇到跨项目引用时,构建整个解决方案比单项目更省心:
dotnet build Chapter_03.slnbuild 不带 --project 时会处理解决方案里所有项目,按依赖顺序编译。无论你之后是 run 还是跑测试,先让整个解决方案能 build 通过,会少踩很多“单独编编不过”的坑。这一点到练习章节尤其关键。
3. 逐章跑通示例:控制台、EF Core 与 Web API 三种项目形态
3.1 控制台与语言特性示例:顶层语句和文件作用域命名空间
C# 10 与 .NET 6 最影响阅读体验的两个语法变化,在仓库前几章里到处都是:顶层语句让 Program.cs 不再需要 class 和 Main 方法;文件作用域命名空间让 namespace 声明直接以分号结尾。
// 文件作用域命名空间:分号结尾,花括号省略 namespace Chapter03.Models; public record Product(string Name, decimal Price) { public string Category { get; init; } = default!; }这段代码值得拆开讲:record 类型由编译器自动生成值相等比较和 with 复制支持,做数据建模很省事;init 表示属性只能在构造过程赋值,构造结束后只读;default! 是告诉编译器“我明确知道这里为空,你别警告”。很多新手在 namespace 行末尾写花括号,C# 10 里这就是语法错误。看出错信息时,优先看是不是这种新旧语法混用导致的。
控制台章节直接运行入口项目即可:
dotnet run --project Chapter_03/Chapter03.csproj运行后看输出,再回到书里对应小节,把代码改几个字跑一遍,比干读书有效得多。仓库的价值在这里体现得最充分:你可以放心地改坏它然后删掉重来,想恢复原状时拉一下 git 就能还原。
3.2 EF Core 与 SQLite:数据访问章节的还原步骤
数据访问章节是仓库依赖最重的一环,典型依赖是 Microsoft.EntityFrameworkCore.Sqlite 和 Microsoft.EntityFrameworkCore.Design。首次还原这些包时要联网,也是暴露源配置问题的高发区:
# 在解决方案目录下还原全部依赖 dotnet restore # 如果还没装 EF 全局工具,先安装,版本与仓库依赖对齐 dotnet tool install --global dotnet-ef --version "6.0.*" # 创建迁移;--project 指向包含 DbContext 的项目(以实际为准) dotnet ef migrations add InitialCreate --project Northwind.Data参数说明:dotnet ef 是独立的全局工具,不在 SDK 里,安装后要重开终端才能直接用。migrations add 需要项目里装了 Microsoft.EntityFrameworkCore.Design 包,缺了会报 “Unable to create migration”。如果 DbContext 所在的类库项目不是可执行项目,还要用--startup-project指定入口项目,否则 EF 工具找不到启动方式。这两个参数经常被漏掉,是数据章节最常见的编译失败原因。
如果仓库某一章已经自带了 .db 文件,就不要跑 migration,直接运行示例读库即可。判断有没有现成数据库文件,用 find 扫一遍:
find . -name "*.db" -print3.3 Web API 章节:启动、端口与环境变量
仓库后半部分以 ASP.NET Core 项目为主,运行形态和控制台完全不同:
# 启动 Web 项目(以实际项目名为准) dotnet run --project Northwind.WebApi启动成功后命令行会打印监听地址,一般是 https://localhost:7xxx 或 http://localhost:5xxx,浏览器直接访问。端口由 launchSettings.json 控制,这个文件只在开发环境生效:
{ "profiles": { "http": { "applicationUrl": "http://localhost:5060" } } }改端口改这里最直接,改完重启项目即可。Web 项目如果启动时报数据库连接失败,先确认前面章节的 .db 文件是否已经生成,再看连接字符串里的路径是不是指向当前工作目录,这两个问题经常连锁出现。
调试时用 hot reload 更顺手:
dotnet watch run --project Northwind.WebApiwatch 监听文件变化自动重启,改控制器或页面时比手动 Ctrl+C 再 run 高效得多,控制台项目同样适用。学到这里,你会发现仓库里的项目形态越到后面越复杂,但启动命令始终是这几种,掌握规律后换哪一章都不慌。
4. 把章节练习当考场:习题与答案项目的对照用法
4.1 先分清示例代码和练习答案两类目录
仓库目录大致分两类职责:一类是书里正文示例的代码快照,你照着书敲代码时以它为准;另一类是每章末尾练习题的参考答案,通常放在名称带 Exercises 或 Answers 意思的目录或解决方案里,具体位置以 clone 下来的说明为准。两者不能混用。
原因很简单:示例代码对应印刷章节,是“完整可编译”的版本,书页里为了排版会省略 using、省略错误处理,仓库里补齐了;答案代码默认你已经掌握本章内容,注释少、写法更简,直接抄会让你跳过练习的过程。更关键的是,答案项目经常引用前面章节的类库,单独编译很容易失败,这不是你写错了,是依赖链断在别处。我见过不少人卡在“抄答案都编译不过”上,其实是把答案项目孤立出来编译导致的。
4.2 一套不抄答案的练习流程
如果目标真是“学完能自己写”,我建议 clone 两份仓库,一份保持纯净:
# 第一份:只读对照,永远不改 git clone <仓库地址> cs10dotnet6 # 第二份:练习工作区,随便折腾 git clone <仓库地址> cs10dotnet6-practice练习流程是:读题 → 在练习副本里自己写 → 编译不过先对照示例代码而不是答案 → 写完再用 diff 与答案对比。
# 对比两个目录的文件差异清单(只看文件名层面) diff -rq cs10dotnet6-practice/Chapter_04 cs10dotnet6/Chapter_04 # 单独看某个文件的内容差异 diff cs10dotnet6-practice/Chapter_04/Program.cs cs10dotnet6/Chapter_04/Program.cs说明:diff -rq 只列出哪些文件不同,适合先定位差异范围;内容层面建议按文件逐个 diff,一上来全量对比会把大量格式差异混进来。如果你自己练习时改了命名空间,diff 时记得忽略命名空间的差异,否则满屏都是无关的差异行,真正的问题反而看不见。
4.3 依赖顺序:答案项目为什么经常编译不过
跨章节引用是这个仓库最容易被忽略的结构特征。某章练习答案可能用到了上一章定义的扩展方法或模型类,你把答案项目单独拷出来 build,编译器会报“类型或命名空间不存在”。这个错误和代码本身无关,纯粹是 ProjectReference 的相对路径失效了。看项目文件里的引用节点就能定位:
# 查看某项目引用了哪些本地项目 grep -A 2 "ProjectReference" Chapter_05/Chapter05.csproj解决思路不是去猜,而是从仓库整体看待依赖:
# 在仓库根目录逐个 build 章节解决方案 for sln in Chapter_0*.sln; do dotnet build "$sln" done没有任何匹配 .sln 的目录会被 glob 原样传给 dotnet,报一个无效参数错误,直接忽略即可,或者先把目录存在性判断再加进循环。全量 build 的真正价值不是帮你通过,而是快速暴露哪些章节的依赖没打通、哪些项目需要先 restore。把仓库从一个系统搬到另一个系统,或者从 6.0 往上做升级验证时,先跑这轮全量 build,能省掉大量零散排查时间。
5. 仓库跑不起来怎么办:五个高频坑与排查命令
这一章按构建和运行两个阶段分类,记录我在不同操作系统上实际遇到的五个高频问题。每个都按现象、原因、解决的顺序写,方便你对照定位。
5.1 构建阶段:目录歧义与 SDK 版本错位
坑 1:目录下有多个项目,dotnet run 报 MSB1003。现象是进入某章目录直接敲 dotnet run,报错 “MSB1003: Specify a project or solution file”,而目录里明明有 .csproj。原因是当前目录下的 .csproj 不止一个,dotnet 不知道该选哪个。解决方法是显式指定入口项目:
dotnet run --project Chapter_05/Chapter05.csproj判断谁是入口,看项目文件的 OutputType 是不是 Exe;只有类库项目时 dotnet run 同样会拒绝执行,因为它没有可运行目标。
坑 2:global.json 锁定的 SDK 本机没有,命令全部失效。现象是在仓库目录内执行任何 dotnet 命令,都报“找不到 SDK”或 NETSDK1045,连 dotnet --version 都不正常。原因是根目录的 global.json 指定了某个 6.0 小版本,本机没装。解决方式是安装对应 SDK,或者临时把文件改名并重开终端。注意:这个报错只在仓库目录范围内发生,在仓库外执行 dotnet --version 是正常的,别被这一点迷惑,很多人因此误以为 SDK 整体坏了。
坑 3:答案项目单独编译报“类型不存在”。现象是把某章答案目录单独拿出来 build,编译器报 CS0103 或找不到命名空间。原因在 4.3 已经说过:答案项目用 ProjectReference 引用了前面章节的类库,单独复制后相对路径失效。解决方式是回到仓库完整目录里 build,或把被引用的类库项目一起复制过去。如果你在 grep ProjectReference 后看到引用路径里有..\这种向上跳转的写法,基本可以断定就是这个原因。
5.2 运行阶段:SQLite 原生库与数据库路径
坑 4:SQLite 原生库加载失败,报 DllNotFoundException e_sqlite3。现象是数据相关章节的程序一启动就崩,异常指向 e_sqlite3。原因是 SQLitePCLRaw 的原生库依赖系统环境,在精简的 Linux 环境可能缺少系统库,在 macOS 上可能是架构不匹配。解决方法是显式指定运行时标识再运行:
# Apple Silicon 上指定 arm64 架构运行入口项目(项目名以实际为准) dotnet run --project Northwind.Console -r osx-arm64 # Linux 服务器上指定 x64 架构 dotnet run --project Northwind.Console -r linux-x64如果还是崩,检查项目引用的 SQLitePCLRaw.bundle_e_sqlite3 包版本是否与 EF Core 6 配套,不配套时还原出来的原生库版本不一致,也会出现同类异常。这个问题在 macOS 上最容易踩,因为 Intel 和 Apple Silicon 两代机器的行为还不一样。
坑 5:数据库文件没出现在预期目录。现象是跑完迁移或示例后,find 找不到 .db 文件,或者文件出现在一个莫名其妙的位置。原因是 SQLite 连接字符串里的相对路径是相对当前工作目录解析的,而 dotnet run 的工作目录可能和项目目录不一致。解决方式是用 find 先确认文件实际落位:
find . -name "*.db" -print找到后再决定改连接字符串为绝对路径,还是统一从固定目录启动。这个坑在 Web 项目里尤其常见,因为 launchSettings.json 里的 workingDirectory 设置会改变运行时的工作目录,把路径问题藏得更深。
提示:五个坑里至少有两个是版本问题。机器上同时存在多个 SDK 时,先执行 dotnet --list-sdks,再决定要不要动 global.json,能省下最多的无效排查时间。
6. 把仓库变成学习脚手架:用 git 对比命令读章节增量
这个仓库最好的用法不是“查答案”,而是当成可检索的题目集和脚手架。这里分享一个我一直在用的技巧:用 git 自带的对比能力,把相邻两章代码的变化当作“作者在这个版本里引入的新概念地图”。
# 看仓库提交历史,找章节相关的提交点 git log --oneline --all | head -50 # 对比某个章节目录在两次提交之间的差异 git diff HEAD~1 HEAD -- Chapter_02如果仓库的提交历史没有按章节整理,很多书籍仓库只在发布时打一个 tag,那退而求其次用目录对比:
diff -rq Chapter_02 Chapter_03只列出变化的文件,这一条命令往往比读书更直观:你看到新增了哪个模型文件、删掉了哪个辅助类,就能猜到这一章真正要讲的东西。比如从控制台章到数据访问章,diff 结果里突然冒出一堆 migrations 目录,你就知道这一章的主线是 EF Core 的迁移机制。
遇到零散语法特性时,直接在仓库里全量搜索:
# 在本仓库所有 cs 文件里查某个语法关键字 grep -rn "record\|init\|with" --include="*.cs" | sort这比搜索引擎靠谱,因为这是作者自己惯用的写法,每个出现位置都和书里的讲解一一对应。我现在的习惯是把仓库当成自己的示例黑板:每学完一章,不把答案贴进笔记,而是用自己的语言重写一遍示例再提交到自己的分支,等隔天再看 diff,留下来的印象远比抄答案深。某个数据访问问题我花了一下午解决,就把结论写成注释钉在代码里,下次遇到同类问题翻仓库比翻书快。这也是我一直保留这个仓库的原因。希望帮到你。
本文还有配套的精品资源,点击获取