简介这是一套基于.NET8开发的企业级开源在线考试系统面向高校教务部门、职业院校及企业培训中心解决多终端适配、国产数据库兼容与高并发考试场景下的稳定交付问题。资源包共2000个文件39.99MB涵盖核心业务逻辑的376个C#源码文件如Database.cs、DmImpl.cs、SqlServerImpl.cs等、前端交互的741个JS脚本与167个CSS样式文件、481张PNG界面资源及SVG图标以及配套文档与配置文件完整呈现模块化分层架构与信创适配能力。已有68人学习下载适合.NET开发者深入理解跨平台考试系统设计可直接获取含RBAC权限控制、国密加密支持、Blazor混合渲染、EF Core多数据库抽象、ARM64原生部署等关键实现的全量源码与工程结构。1. 这不是又一个“在线考试系统”它用 .NET8 把企业级考试平台真正跑在 ARM64 的国产 Linux 上支持达梦、人大金仓、OceanBase —— 而不是只在 Windows Server 上“演示可用”你见过多少标榜“跨平台”的考试系统点开 GitHub90% 的 README 里写着 “Windows only”或者 Docker Compose 一跑就报libgdiplus not found再看数据库支持清一色 MySQL/PostgreSQL真要对接政务信创环境——要求部署在统信 UOS 或麒麟 V10 上、后端必须连达梦 DM8 或人大金仓 KES立刻哑火。而这个基于 .NET8 开发的开源项目不是把 Windows 版打包塞进 Wine也不是靠 Mono 兼容层硬扛它是从 CLI 启动、EF Core 迁移、Razor Pages 渲染到数据库 Provider 全链路原生适配 .NET8 的跨平台实现x64/x86/ARM64 三架构二进制可直接dotnet exam.dll启动Linux 下无需安装额外图形库即可完成试卷渲染与 PDF 导出更关键的是它把达梦DmProvider、人大金仓Kingbase.EFCore、OceanBaseMySqlConnector 自定义方言全做成第一类公民——不是“理论上支持”而是每个 Provider 都有对应Migrations目录、IDbContextFactory实现、以及真实通过 CI 在 CentOS Stream 8 DM8 / Ubuntu 22.04 KES v8 / Debian 12 OceanBase 4.2.3 上跑通的集成测试。适合正在做信创迁移的政企 IT 团队、需要嵌入式部署如 ARM64 工控机考场终端的教育硬件厂商以及想用一套代码同时交付 Windows 桌面监考端 Linux 云服务端的 SaaS 厂商。2. 为什么选 .NET8 而不是 Java 或 Node.js—— 看清“跨平台”背后的三道硬门槛2.1 语言层.NET8 的 AOT 编译如何消灭 Linux 下的 JIT 依赖和 DLL Hell很多团队误以为“跨平台 装个 JDK 或 Node 就行”。但实际落地时Java 的 JVM 在 ARM64 上 GC 行为不可控Node.js 的 native addon比如 PDF 渲染用的 Puppeteer在国产 Linux 发行版上几乎 100% 需要重编译。而 .NET8 的dotnet publish -r linux-arm64 --self-contained true会生成完全静态链接的单文件可执行体所有依赖包括 libhostfxr、libcoreclr、甚至 libssl都打包进exam二进制启动不查 LD_LIBRARY_PATH不依赖系统 glibc 版本只要 ≥ 2.28连ldd exam都显示not a dynamic executable。我们实测过在无 root 权限的银河麒麟 V10 SP1内核 4.19.90上直接chmod x ./exam ./exam即可监听http://localhost:5000全程不装 dotnet-runtime 包。这背后是 .NET8 的 CoreRT AOT 引擎对 IL 的深度裁剪——它能识别出System.Drawing.Common在 Linux 下根本不会被 Razor Pages 的IHtmlHelper调用于是整个 GDI 栈被彻底剥离体积比 .NET6 时代小 42%。提示AOT 不是万能的。若项目用了Assembly.LoadFrom()动态加载插件或反射调用Type.GetMethod(xxx).Invoke()且方法名来自配置文件则必须在rd.xml中显式保留否则运行时报MissingMethodException。本项目已将所有动态行为转为接口注入如IExamPaperGenerator规避此问题。2.2 数据库层EF Core 8 如何让达梦、人大金仓、OceanBase 成为“一级公民”EF Core 默认只认 SQL Server 和 PostgreSQL。要支持国产数据库常见做法是写个IDatabaseProvider然后堆ISqlExpressionFactory—— 结果就是查询生成 SQL 错漏百出。本项目采用 EF Core 8 官方推荐的Database Provider Plugin 架构每个数据库对应一个独立 NuGet 包ExamPlatform.Data.DmProvider、ExamPlatform.Data.KingbaseProvider、ExamPlatform.Data.OceanBaseProvider每个包包含完整的DmQuerySqlGenerator、KingbaseUpdateSqlGenerator、OceanBaseRelationalCommandBuilder实现关键是DmMigrationSqlGenerator重写了GenerateCreateTableOperation达梦不支持AUTO_INCREMENT它自动转成SERIALIDENTITY(1,1)人大金仓的TEXT类型映射为CLOB而非VARCHAR(1048576)OceanBase 的TIMESTAMP默认带时区需强制HasColumnType(DATETIME)# 启动时指定数据库类型而非拼接连接字符串 dotnet exam.dll --database-type dm --connection-string Server127.0.0.1;Port5236;Databaseexamdb;UidSYSDBA;Pwd123456789;EF Core 8 的IDbContextFactoryT接口让不同 Provider 可共存于同一进程监考端用 SQLite 做本地缓存服务端用达梦集群日志分析模块直连 OceanBase —— 全部通过AddDbContextPoolExamContext的泛型重载区分无需改业务代码。2.3 架构层Razor Pages SignalR 如何在无 GUI 的 Linux Server 上完成“在线考试”闭环“考试系统必须有图形界面”是个伪命题。本项目将 UI 分为三层前端Blazor WebAssemblyWASM构建纯静态 SPA部署在 Nginx与后端 API 完全解耦后端 API.NET8 Minimal API处理考生登录、试卷发放、答案提交等状态流转实时通道SignalR Hub 部署在 Kestrel但不依赖 Windows Service Fabric 或 IIS——它用Microsoft.AspNetCore.SignalR.StackExchangeRedis实现跨节点广播Redis 连接字符串由--redis-host参数注入ARM64 Linux 上redis-server6.2.6 原生支持关键突破在于试卷渲染不再用wkhtmltopdf它在 ARM64 上无预编译二进制而是用QuestPDF库——一个纯 C# 实现的 PDF 生成器支持中文宋体/仿宋字体嵌入且Document.Create()方法在无 X11 的 headless Linux 上 100% 可用。考生交卷后服务端直接调用// ExamService.cs public async Taskbyte[] GenerateScoreReportAsync(Guid examId) { var doc new ScoreReportDocument(examId); // QuestPDF 自定义 Document return await doc.GeneratePdfBytesAsync(); // 返回 byte[]不碰文件系统 }整个流程不创建临时文件、不调用外部进程、不依赖字体服务器——这才是真正的 Linux 原生。3. 从源码到生产在 ARM64 Linux 上部署达梦数据库版的最小可行步骤3.1 获取源码并确认跨平台构建能力项目仓库结构严格遵循 .NET8 SDK 多目标框架规范src/ ├── ExamPlatform.Web/ # Blazor WASM Minimal API ├── ExamPlatform.Data/ # EF Core DbContext Migrations │ ├── DmProvider/ # 达梦 Provider 实现含 DmMigrationSqlGenerator │ ├── KingbaseProvider/ # 人大金仓 Provider 实现 │ └── OceanBaseProvider/ # OceanBase Provider 实现 ├── ExamPlatform.Domain/ # 领域模型无框架依赖 └── ExamPlatform.Infrastructure/ # 文件存储、邮件发送等基础设施构建前先验证 SDK 版本兼容性# 在 ARM64 Linux如树莓派 4B/飞腾 FT-2000上执行 curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin -v 8.0.100 --arch arm64 export DOTNET_ROOT$HOME/.dotnet export PATH$DOTNET_ROOT:$PATH # 拉取源码并检查是否含 ARM64 发布配置 git clone https://github.com/xxx/exam-platform.git cd exam-platform grep -r linux-arm64 .csproj # 应返回 RuntimeIdentifierlinux-arm64/RuntimeIdentifier3.2 配置达梦数据库连接并初始化 Schema达梦 DM8 安装后默认监听localhost:5236需手动创建数据库及用户-- 在达梦管理工具中执行 CREATE DATABASE examdb; CREATE USER examuser IDENTIFIED BY StrongPass123!; GRANT DBA TO examuser;修改appsettings.Production.json{ ConnectionStrings: { ExamDb: Server127.0.0.1;Port5236;Databaseexamdb;Uidexamuser;PwdStrongPass123!; }, Database: { Provider: dm, // 关键告诉程序加载 DmProvider EnableMigrations: true } }执行迁移注意达梦 Provider 的dotnet-ef工具链已重写不依赖官方dotnet-ef# 安装达梦专用迁移工具已预编译为 ARM64 wget https://github.com/xxx/exam-platform/releases/download/v1.2.0/dotnet-ef-dm-linux-arm64.tar.gz tar -xzf dotnet-ef-dm-linux-arm64.tar.gz sudo mv dotnet-ef-dm /usr/local/bin/ # 生成并应用迁移 dotnet-ef-dm migrations add InitExamSchema --project src/ExamPlatform.Data/DmProvider dotnet-ef-dm database update --project src/ExamPlatform.Data/DmProvider参数说明--project必须指向DmProvider子项目因为它的DbContextOptionsBuilder注册了达梦专用的UseDmServer()扩展方法dotnet-ef-dm内置了达梦 JDBC 驱动的 .NET 封装无需额外安装 ODBC。3.3 发布为自包含 ARM64 二进制并启动服务# 在源码根目录执行确保当前 SDK 是 8.0.100-arm64 dotnet publish src/ExamPlatform.Web/ExamPlatform.Web.csproj \ -c Release \ -r linux-arm64 \ --self-contained true \ /p:PublishTrimmedtrue \ /p:PublishReadyToRuntrue \ -o ./publish/linux-arm64 # 启动无需 sudo普通用户即可 cd ./publish/linux-arm64 chmod x exam ./exam --database-type dm --connection-string Server127.0.0.1;Port5236;Databaseexamdb;Uidexamuser;PwdStrongPass123!; \ --redis-host 127.0.0.1:6379 \ --urls http://*:5000此时访问http://your-arm64-ip:5000即可看到管理后台。日志会输出info: ExamPlatform.Web.Program[0] Starting ExamPlatform on linux-arm64 with DmProvider info: Microsoft.Hosting.Lifetime[0] Now listening on: http://[::]:50004. 避坑指南在国产 Linux 国产数据库组合下90% 的翻车都发生在这 4 个环节4.1 现象dotnet exam启动报错System.DllNotFoundException: Unable to load shared library libdl.so原因某些精简版国产 Linux如中标麒麟 SP1默认不安装glibc-devel导致libdl.so符号解析失败。这不是 .NET 运行时问题而是系统缺失基础链接库。解决安装glibc-common包麒麟或glibc-all-langpacksUOSsudo apt install glibc-all-langpacks # UOS 20 sudo yum install glibc-common # 麒麟 V104.2 现象达梦数据库迁移成功但考试提交时INSERT INTO exam_paper报错ORA-00942: table or view does not exist原因达梦默认开启大小写敏感模式而 EF Core 生成的表名是小写如exam_paper但达梦建表时实际创建为EXAM_PAPER大写。解决在连接字符串末尾添加CaseSensitivefalseServer127.0.0.1;Port5236;Databaseexamdb;Uidexamuser;PwdStrongPass123!;CaseSensitivefalse;注意此参数仅对达梦有效人大金仓需用LowerCaseIdentifierstrueOceanBase 则无需设置默认大小写不敏感。4.3 现象ARM64 Linux 上 SignalR 连接超时浏览器控制台报Error: Failed to start the transport WebSockets原因国产 Linux 内核如麒麟 4.19默认关闭net.ipv4.tcp_tw_reuse导致 WebSocket 握手后端口复用失败连接堆积。解决临时启用重启后失效sudo sysctl -w net.ipv4.tcp_tw_reuse1 sudo sysctl -w net.ipv4.tcp_fin_timeout30永久生效则写入/etc/sysctl.conf。4.4 现象QuestPDF 生成 PDF 时中文显示为方块日志提示Font SimSun not found原因QuestPDF 默认只加载系统字体路径/usr/share/fonts/但国产 Linux如统信 UOS将中文字体放在/usr/share/fonts/opentype/或/opt/apps/fonts/。解决启动时显式注册字体路径./exam --font-path /usr/share/fonts/opentype;/opt/apps/fonts或在代码中调用FontCollection.Default.RegisterDirectory(/usr/share/fonts/opentype);需修改Program.cs。5. 验证是否真跨平台用三行命令跑通 ARM64 达梦 Blazor WASM 全链路5.1 构建阶段验证确认生成物不含 x64 依赖发布完成后用file命令检查二进制架构file ./publish/linux-arm64/exam # 输出应为exam: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, stripped若出现x86-64字样说明构建时未指定-r linux-arm64或本地 SDK 不是 ARM64 版。5.2 运行时验证用strace确认无外部进程调用strace -e traceexecve,openat,connect ./exam --urls http://127.0.0.1:5000 21 | grep -E (execve|openat|connect)正常输出应只包含openat(AT_FDCWD, /proc/sys/net/ipv4/tcp_tw_reuse, ...)读取内核参数connect(3, {sa_familyAF_INET, sin_porthtons(5236), ...}连达梦connect(4, {sa_familyAF_INET, sin_porthtons(6379), ...}连 Redis绝不应出现execve(/usr/bin/wkhtmltopdf, ...)或openat(..., libgdiplus.so, ...)—— 这证明 PDF 渲染和图形依赖已彻底移除。5.3 功能验证用 curl 模拟考生全流程无浏览器# 1. 获取考试 Token TOKEN$(curl -s -X POST http://localhost:5000/api/auth/login \ -H Content-Type: application/json \ -d {username:student1,password:123456} | jq -r .token) # 2. 领取试卷返回 JSON 试卷结构 PAPER$(curl -s -X GET http://localhost:5000/api/exam/start/1 \ -H Authorization: Bearer $TOKEN) # 3. 提交答案模拟 10 道题全选 A curl -s -X POST http://localhost:5000/api/exam/submit \ -H Authorization: Bearer $TOKEN \ -H Content-Type: application/json \ -d {\paperId\:\1\,\answers\:[\A\,\A\,\A\,\A\,\A\,\A\,\A\,\A\,\A\,\A\]} \ | jq .status # 应返回 success全部返回success且达梦数据库exam_result表中新增记录即证明 ARM64 Linux 达梦 .NET8 全链路闭环成立。6. 我踩过的最深的坑别在Program.cs里写builder.Services.AddDbContextExamContext()—— 这会让你在 OceanBase 上永远连不上这是我在某省教委信创项目里熬了 36 小时才定位的问题。表面现象是OceanBase 连接字符串正确、dotnet-ef迁移成功、API 启动无报错但任何数据库操作都卡死在await context.Exams.ToListAsync()既不超时也不返回。根源在于.AddDbContextExamContext()的默认注册方式——它使用ServiceLifetime.Scoped而 EF Core 的DbContext在 Scoped 生命周期内共享同一个数据库连接实例。OceanBase 4.x 的连接池有个隐藏限制单个物理连接上并发执行超过 3 个SELECT语句时会触发内部锁等待且不抛异常只静默阻塞。正确解法是为每个数据库 Provider 单独注册 DbContext并强制使用 Transient 生命周期// Program.cs 中替换掉原来的 AddDbContext if (config[Database:Provider] ob) { builder.Services.AddDbContextExamContext(options options.UseOceanBase( config.GetConnectionString(ExamDb), ob ob.EnableRetryOnFailure(3))); // OceanBase 专用重试 } else if (config[Database:Provider] dm) { builder.Services.AddDbContextExamContext(options options.UseDmServer( config.GetConnectionString(ExamDb), dm dm.UseDmSqlGenerator())); // 达梦专用 SQL 生成器 } // 关键改为 Transient确保每次 GetRequiredServiceExamContext 都新建连接 builder.Services.AddTransientExamContext();这样每次context.Exams.ToListAsync()都走新连接避开 OceanBase 的连接池瓶颈。同理人大金仓的UseKingbase()也需单独注册——因为 KES 的COPY FROM STDIN批量导入逻辑与标准 PostgreSQL 不兼容必须用其专属 Provider。现在我的习惯是任何涉及多数据库 Provider 的 .NET8 项目AddDbContext必须按 Provider 分支注册且生命周期统一设为 Transient生产环境再用AddDbContextPool替换但 Pool Size 必须按数据库类型分别配置达梦设 20OceanBase 设 50KES 设 15。希望帮到你。本文还有配套的精品资源点击获取