简介:星期八在线考试系统是一套基于.NET8构建的开源、免费、跨平台企业级在线考试软件,面向高校、职业院校、党政机关培训中心及大型国企教育平台的技术选型与二次开发人员,可解决多架构、多数据库环境下的考试系统部署与信创适配问题。资源包共2000个文件,约39.99MB,以376个cs源码、741个js脚本、167个css样式、481个png与80个gif界面素材为主,另含54个html页面、14个docx文档及csproj、sln工程文件,覆盖用户管理、题库、试卷生成、自动阅卷、成绩分析与权限控制等模块。系统原生支持x64、x86、Arm64架构的Windows与Linux,兼容人大金仓、达梦、OceanBase及MySQL、SQL Server、PostgreSQL、SQLite,并集成JWT认证、RBAC+ABAC权限、防切屏与随机乱序等安全机制。目前已有68人学习下载,适合研究.NET8分层架构、EF Core多库适配与高并发考试场景的开发者参考。
1. 从一台飞腾笔记本到国产数据库:这套 .NET8 在线考试系统到底能扛什么活
去年帮一所职业院校做机房改造,对方提了个很具体的需求:考试系统要跑在统信 UOS 的飞腾笔记本上,后台数据库用学校已经买好的人大金仓,而且不能依赖任何外部云服务。我当时第一反应是"这活儿不好接"——市面上大多数在线考试软件要么绑死 Windows + SQL Server,要么是 SaaS 形态根本不给本地部署。后来翻到这套基于 .NET8 开发的开源企业级在线考试软件,才算把方案落地。它最大的价值不在于功能多花哨,而在于把"跨平台"和"国产数据库适配"这两件在国内政企、院校场景里最要命的事做进了架构层:同一套代码能编出 x64、x86、Arm64 三种芯片架构的 Windows 和 Linux 可执行程序,数据库层同时支持人大金仓、达梦、OceanBase 这些国产库。如果你正好在做教学管理系统的选型,或者需要一套能私有化部署、能过信创验收的在线考试系统,这套东西值得花时间拆一遍。下面我按"它是什么、怎么跑起来、坑在哪、怎么用深"的顺序,把实际部署和二次开发里踩过的东西讲清楚。
2. 拆开这套 .NET8 考试系统的技术骨架:跨平台和国产库适配是怎么做到的
2.1 为什么选 .NET8 而不是 Java 或 Node 做考试系统
在线考试系统这类应用有个特点:并发峰值集中(一场考试几百上千人同时交卷)、业务逻辑重(组卷算法、判分规则、防作弊策略)、部署环境杂(院校机房、政企内网、信创终端)。选 .NET8 做底座,核心原因是它在"单文件发布 + 原生 AOT + 跨架构编译"这条链路上比 Java 和 Node 更省事。Java 要跨 Arm64 得靠不同 JDK 发行版,Node 的原生模块在 Arm 上经常要重新编译,而 .NET8 的dotnet publish -r linux-arm64一条命令就能出目标架构的可执行文件,运行时自包含,目标机器不需要预装任何运行时。
另一个现实考量是国产化适配。人大金仓、达梦这些数据库厂商对 .NET 的驱动支持这两年补得比较快,达梦有官方的 DmProvider,人大金仓基于 PostgreSQL 内核所以能复用 Npgsql 生态,OceanBase 也提供了 MySQL 兼容模式。相比之下,某些小众语言生态的驱动要么没有,要么版本滞后。这套系统能在数据库层做多适配,本质上是吃到了 .NET 生态里这些驱动成熟的红利。
从架构上看,它走的是典型的分层单体:Web API 层(ASP.NET Core)、业务服务层、数据访问层(通过 ORM 抽象屏蔽数据库差异)、前端(常见做法是 Vue 或 React 打包后由后端托管)。这种结构对二次开发友好,你要换数据库主要动数据访问层的配置和少量方言处理,不用重写业务逻辑。
2.2 数据库适配层:一套代码怎么同时喂饱金仓、达梦和 OceanBase
多数据库适配最容易翻车的地方不是连接字符串,而是 SQL 方言差异。分页语法、自增主键、日期函数、字符串拼接,这四类几乎每个库都不一样。这套系统的做法是在 ORM 之上做了一层方言适配,常见方案是 Entity Framework Core 配合数据库特定的 Provider,把差异收敛到配置里。
下面是我在人大金仓环境下验证连接的一段配置代码,用的是 EF Core + Npgsql(金仓兼容 PostgreSQL 协议):
// Program.cs 中注册数据库上下文 builder.Services.AddDbContext<ExamDbContext>(options => { // 人大金仓走 PostgreSQL 协议,端口默认 54321 options.UseNpgsql( builder.Configuration.GetConnectionString("KingbaseConnection"), npgsqlOptions => { // 金仓对某些 EF Core 迁移特性支持滞后,关闭自动迁移 npgsqlOptions.MigrationsAssembly("ExamSystem.Data"); npgsqlOptions.CommandTimeout(60); // 组卷查询可能较慢,超时放宽 }); });// appsettings.json 连接字符串示例 { "ConnectionStrings": { "KingbaseConnection": "Host=10.0.1.20;Port=54321;Database=exam_db;Username=exam_user;Password=YourPassword;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50" } }逻辑说明:UseNpgsql是告诉 EF Core 用 PostgreSQL 方言生成 SQL,金仓因为内核兼容所以能直接吃。CommandTimeout设 60 秒是因为组卷时可能涉及多表关联和随机抽题,默认 30 秒在题量大的时候会超时。连接池的Maximum Pool Size设 50 是经验值——一场 500 人考试,每人交卷时会有一次写操作,池子太小会排队,太大又浪费数据库连接。
参数怎么改:端口号金仓默认 54321,达梦默认 5236,OceanBase 的 MySQL 模式默认 2881。达梦要用DmProvider而不是 Npgsql,连接字符串里Host换成Server,语法略有不同。切换数据库时,除了改连接字符串,还要检查迁移脚本——不同库的建表语句不通用,我一般会针对每个目标库单独维护一份初始化 SQL。
提示:国产数据库的驱动版本要和数据库服务端版本对齐,达梦 DM8 用 DmProvider 8.x,版本错配会出现"连接成功但查询报错"的玄学问题。
2.3 跨架构发布:x64、x86、Arm64 三套产物怎么出
这是这套系统最能打的地方。国内信创终端芯片架构不统一,飞腾、鲲鹏是 Arm64,兆芯、海光是 x86,老机房还有一堆 x64。如果每个架构都要单独维护一套代码,维护成本会爆炸。.NET8 的 RID(Runtime Identifier)机制让同一份代码编出不同架构产物成为可能。
# 发布 Linux x64 自包含版本 dotnet publish -c Release -r linux-x64 --self-contained true \ -p:PublishSingleFile=true -o ./publish/linux-x64 # 发布 Linux Arm64(飞腾、鲲鹏终端用) dotnet publish -c Release -r linux-arm64 --self-contained true \ -p:PublishSingleFile=true -o ./publish/linux-arm64 # 发布 Windows x64 dotnet publish -c Release -r win-x64 --self-contained true \ -p:PublishSingleFile=true -o ./publish/win-x64 # 发布 Windows x86(老机房 32 位系统) dotnet publish -c Release -r win-x86 --self-contained true \ -p:PublishSingleFile=true -o ./publish/win-x86逻辑说明:--self-contained true表示把 .NET 运行时一起打包,目标机器不用装 SDK 或运行时,这对内网隔离环境是刚需。PublishSingleFile把所有 DLL 合成一个可执行文件,部署时拷贝一个文件就行,减少"少拷了某个依赖"的翻车。-r指定 RID,决定了编译出的二进制架构。
参数说明:如果目标机器是国产 Linux 发行版(统信 UOS、麒麟),linux-x64和linux-arm64一般都能直接跑,但要注意 glibc 版本——老版本 UOS 的 glibc 可能低于 .NET8 要求,这种情况要么升级系统库,要么用-p:PublishTrimmed配合容器化部署。Arm64 产物在飞腾上跑之前,建议先用file命令确认二进制架构,避免拿错产物。
3. 从零跑起来:部署、初始化和第一场考试的完整操作链
3.1 环境准备与依赖清单
在动手之前,先把环境对齐。这套系统对运行时的要求不算高,但国产数据库这块有几个前置条件容易漏。
| 组件 | 版本要求 | 说明 |
|---|---|---|
| .NET 运行时 | 8.0 | 自包含发布可免装,但开发机要装 SDK |
| 数据库 | 金仓 V8 / 达梦 DM8 / OceanBase 4.x | 三选一,按现有环境定 |
| 操作系统 | Windows 10+ / 主流 Linux 发行版 | 信创终端需确认 glibc 版本 |
| 内存 | 最低 4GB,建议 8GB | 组卷和判分吃内存 |
| 反向代理 | Nginx(Linux 场景) | 处理静态资源和 HTTPS |
数据库这块我重点说达梦,因为它的安装和连接最容易出问题。达梦 DM8 在 Windows 上的安装包是dm8数据库安装包x86_win_64.zip这类命名,安装时注意两点:一是实例端口默认 5236,二是要记牢 SYSDBA 密码,后面建库建用户都要用。Linux 上装达梦通常是命令行静默安装,装完用disql工具连上去验证。
# 达梦数据库连接验证(Linux 下用 disql) disql SYSDBA/SYSDBA@localhost:5236 # 连上后执行 SELECT * FROM V$VERSION;逻辑说明:disql是达梦自带的命令行客户端,格式是用户名/密码@主机:端口。能查出V$VERSION说明服务端正常。如果报"网络通信异常",先查防火墙和端口监听,netstat -tlnp | grep 5236。
3.2 数据库初始化:建库、建用户、导表结构
系统首次启动前要先把数据库结构建好。这套系统一般会提供初始化 SQL 脚本,但针对不同数据库的脚本是分开的,别拿错。
-- 达梦环境下建用户和表空间(用 SYSDBA 执行) CREATE USER EXAM_USER IDENTIFIED BY "Exam@2024"; CREATE TABLESPACE EXAM_DATA DATAFILE 'exam_data.dbf' SIZE 512 AUTOEXTEND ON; ALTER USER EXAM_USER DEFAULT TABLESPACE EXAM_DATA; GRANT RESOURCE, PUBLIC TO EXAM_USER;逻辑说明:达梦的建用户语法和 Oracle 接近,IDENTIFIED BY后面密码要加双引号(如果含特殊字符)。TABLESPACE单独建是为了把考试数据和系统数据分开,方便后续备份和扩容。GRANT RESOURCE给的是建表权限,不给的话导入表结构会报权限不足。
参数说明:SIZE 512是初始 512MB,AUTOEXTEND ON表示自动扩展,考试系统数据增长主要在答题记录和日志表,题库存量一般不大。如果学校规模大(上万学生),初始可以给 2GB。
导表结构时,用系统自带的schema_dm.sql(达梦版),通过 disql 执行:
disql EXAM_USER/Exam@2024@localhost:5236 \`schema_dm.sql\`注意:导入前确认 SQL 脚本里的表空间名和实际建的一致,不一致会报"表空间不存在",这个错我见过好几次,都是脚本没改直接跑。
3.3 启动服务与首次登录验证
数据库就绪后,改配置文件里的连接字符串,然后启动。
# Linux 下启动(自包含发布产物) cd ./publish/linux-x64 chmod +x ExamSystem.Web ./ExamSystem.Web --urls "http://0.0.0.0:5000"逻辑说明:--urls指定监听地址,0.0.0.0表示所有网卡都可访问,内网部署常用。如果只想本机访问用127.0.0.1。端口 5000 是默认值,和 Nginx 配合时通常改成 5001 避免冲突。
启动后浏览器访问http://服务器IP:5000,默认管理员账号一般在初始化脚本或文档里(常见是 admin/admin123 这类),首次登录后立刻改密码。进去先做三件事:建考试分类、导入题库、配一场模拟考试跑通全流程。题库导入支持 Excel 模板,常见做法是下载模板填好再上传,注意题目类型和选项列的格式,格式不对会整批导入失败。
4. 避坑指南:国产数据库适配和跨平台部署里最容易翻车的五件事
4.1 达梦连接报"无效的用户名或密码"
现象:连接字符串确认没写错,但用 Navicat 或程序连达梦都报认证失败。
原因:达梦默认对密码大小写敏感,而且有些版本在创建用户时如果密码没加双引号,会被自动转成大写存储。另外达梦有个"密码策略"参数,多次失败会锁账号。
解决:先用 disql 用 SYSDBA 登进去,SELECT * FROM DBA_USERS WHERE USERNAME='EXAM_USER'确认用户存在。如果密码不确定,直接ALTER USER EXAM_USER IDENTIFIED BY "新密码"重置。程序侧连接字符串里密码原样写,别自作主张加引号。Navicat 连达梦需要单独装达梦驱动,不是 MySQL 驱动能凑合的。
4.2 Arm64 产物在飞腾机器上启动即崩
现象:./ExamSystem.Web执行后秒退,没有任何日志。
原因:九成是 glibc 版本不匹配。.NET8 要求 glibc 2.28+,部分老版本统信 UOS 或麒麟自带的是 2.17 或 2.23。
解决:ldd --version查 glibc 版本。低于 2.28 的话,要么升级系统(信创环境升级要审批,麻烦),要么改用框架依赖发布 + 在目标机器装对应版本的 .NET 运行时(运行时对 glibc 要求略低),要么上容器。我一般优先推荐容器方案,把 glibc 依赖封在镜像里,一劳永逸。
4.3 人大金仓分页查询结果错乱
现象:题库列表翻页时,第二页出现第一页的题,或者某些题重复出现。
原因:金仓在兼容 PostgreSQL 时,某些版本的LIMIT/OFFSET在没有稳定排序的情况下返回顺序不确定。EF Core 生成的分页 SQL 如果没带ORDER BY,就会踩这个坑。
解决:所有分页查询强制加稳定排序字段,通常是主键或创建时间。在 EF Core 里就是.OrderBy(x => x.Id)不能省。这个坑在 SQL Server 上不明显(它默认有聚集索引顺序),换到金仓就暴露了。
4.4 OceanBase 连接池耗尽导致交卷超时
现象:考试高峰期,学生点交卷转圈很久,后台日志报"连接池超时"。
原因:OceanBase 的 MySQL 模式对连接数有限制,默认可能只有几百。考试系统如果没控制并发写入,交卷瞬间大量请求抢连接。
解决:连接字符串里设Maximum Pool Size,同时业务层对交卷操作做队列化或限流。另外 OceanBase 的租户资源要提前扩,Unit规格不够的话,连接数上去了查询也会慢。这个要在考前压测,别等真考试才发现。
4.5 单文件发布后配置文件读不到
现象:PublishSingleFile发布后,程序读不到appsettings.json,用的全是默认值。
原因:单文件发布时,配置文件默认不会被打进 exe,而是散落在输出目录。如果部署时只拷了 exe 没拷 json,或者工作目录不对,就读不到。
解决:部署时把appsettings.json和 exe 放同一目录,启动时用--contentRoot指定工作目录。或者干脆把关键配置通过环境变量注入,容器化部署时这是标准做法。
5. 二次开发进阶:换库、加题型和性能压测的三个实操技巧
先说换库。这套系统的数据访问层做了抽象,但换库不是改个连接字符串就完事。我的习惯是先在目标库上跑一遍初始化脚本,确认表结构建得起来,再改 Provider 注册代码,最后跑一遍核心业务(登录、组卷、交卷、判分)的冒烟测试。达梦和金仓的日期函数差异最大,比如GETDATE()在金仓里是NOW(),达梦里是SYSDATE,如果业务代码里有裸 SQL 用到这些,要逐个替换。EF Core 的 LINQ 查询大部分能自动翻译,但复杂的手写 SQL 要人工过一遍。
加题型是二次开发里最常见的需求。系统一般内置单选、多选、判断、填空、简答,要加"编程题"或"连线题"就得动判分逻辑。我的做法是先把题型抽象成一个接口,判分策略单独实现,别把 switch-case 堆在判分主流程里。下面是个判分策略的骨架:
public interface IQuestionGrader { // 返回得分,满分由题目定义 decimal Grade(Question question, string userAnswer); } public class ProgrammingGrader : IQuestionGrader { public decimal Grade(Question question, string userAnswer) { // 编程题常见做法:编译 + 跑测试用例,按通过率给分 // 注意沙箱隔离,别让考生代码碰到宿主机 var testCases = question.TestCases; int passed = RunInSandbox(userAnswer, testCases); return question.Score * passed / testCases.Count; } }逻辑说明:把判分逻辑接口化之后,新增题型只要实现接口并注册到工厂里,主流程不用改。编程题的沙箱是重点,常见做法是用 Docker 容器跑考生代码,限制 CPU、内存和网络,跑完即销毁。这块如果做不好,就是安全事故。
最后说压测。考试系统的性能瓶颈通常在交卷瞬间的写入和组卷时的随机抽题。我一般用 JMeter 或 k6 模拟 500 并发交卷,观察数据库连接池和响应时间。组卷的随机抽题如果用的是ORDER BY RANDOM(),在数据量大时会全表扫描,正确做法是先用随机 ID 范围查,再取题。压测时重点看三个指标:交卷 P99 响应时间(要控制在 2 秒内)、数据库连接池等待数、CPU 占用。考前一周必须压一遍,我吃过没压测的亏——平时测试几十人没问题,真考试五百人同时交卷,数据库连接池直接打满,最后靠临时扩容才救回来。从那以后我每次上线前都强制走一遍全链路压测,不管校方怎么催。希望这套拆解能帮到你,少走点我走过的弯路。
本文还有配套的精品资源,点击获取