You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#操作SQLite计算GPA时仅读取单门课程成绩的问题排查

问题背景

搭建高校教务系统时,在GpaCal()方法中通过SQLite数据库计算学生指定学期GPA,学生表结构如下:
学生表结构
原实现逻辑为:先查询指定学期的课程总数,初始化对应长度的成绩、学分、加权绩点数组,通过SQLiteDataReader读取查询结果后,在循环内完成分数到绩点的换算,最终累加总加权绩点除以总学分得到GPA。实际运行时无法遍历所有课程记录,仅能读取到单门课程的成绩数据。

问题根因

故障由3个核心逻辑错误导致:

  • 返回语句位置错误:return ClassGpa;被写在while(reader.Read())循环体内部,reader.Read()读到第一条课程记录、跑完内部逻辑后就直接触发返回,循环直接终止,后续所有课程记录不会被读取。
  • 循环嵌套逻辑错误:while(reader.Read())本身的作用是逐行遍历查询返回的每一条课程记录,原代码在该循环内部又嵌套了一层遍历全数组长度的for循环,每读一条记录就从数组下标0开始反复覆盖数组内容,即使调整return位置,最终数组存储的也会是最后一条记录重复N次的错误数据。
  • 边界判断与资源管理隐患:直接拼接SQL语句存在注入风险,数据库连接、Command、DataReader未做资源释放,字段空值未做判断,绩点区间用固定上限(如89.99)判断存在浮点数精度遗漏问题。
修复实现

GPA计算不需要提前查询课程总数、也不需要数组存储所有课程明细,只需要在遍历记录时累计「加权绩点总和」和「总学分」两个核心值即可,修复后代码如下:

public double GpaCal()
{
    double totalWeightedGpa = 0;
    double totalCredit = 0;
    connect();
    try
    {
        // 用参数化查询替代SQL拼接,避免注入风险
        string querySql = $"select grade, ECTS from {student} where term = @termInfo;";
        using (SQLiteCommand command = new SQLiteCommand(querySql, myConnection))
        {
            command.Parameters.AddWithValue("@termInfo", termInfo);
            using (SQLiteDataReader reader = command.ExecuteReader())
            {
                // 逐行遍历所有课程记录
                while (reader.Read())
                {
                    int gradeOrdinal = reader.GetOrdinal("grade");
                    int ectsOrdinal = reader.GetOrdinal("ECTS");
                    // 空值跳过,避免转换异常
                    if (reader.IsDBNull(gradeOrdinal) || reader.IsDBNull(ectsOrdinal))
                        continue;

                    double mark = reader.GetDouble(gradeOrdinal);
                    int credit = Convert.ToInt32(reader.GetDouble(ectsOrdinal));

                    // 左闭右开区间判断绩点,避免浮点数精度问题
                    double coursePoint = mark switch
                    {
                        >= 90 and <= 100 => 4.0,
                        >= 85 and < 90 => 3.5,
                        >= 80 and < 85 => 3.0,
                        >= 75 and < 80 => 2.5,
                        >= 70 and < 75 => 2.0,
                        >= 65 and < 70 => 1.5,
                        >= 60 and < 65 => 1.0,
                        >= 50 and < 60 => 0.5,
                        > 0 and < 50 => 0.0,
                        _ => 0.0
                    };

                    totalWeightedGpa += coursePoint * credit;
                    totalCredit += credit;
                }
            }
        }
    }
    finally
    {
        // 确保连接关闭释放
        if (myConnection.State == System.Data.ConnectionState.Open)
            myConnection.Close();
    }

    if (totalCredit == 0) return 0.0;
    ClassGpa = totalWeightedGpa / totalCredit;
    return ClassGpa;
}
关键修改说明
  • 移除了多余的课程总数预查询、数组初始化逻辑,遍历数据时直接累计计算所需的核心值,减少不必要的内存开销。
  • 将return语句移出数据读取循环,确保所有课程记录遍历完成后再执行最终GPA计算和结果返回。
  • 删除了错误的嵌套for循环,逐行读取时直接处理当前行的成绩、学分数据,不存在数组值被反复覆盖的问题。
  • 新增参数化查询、空值判断、资源释放逻辑,规避SQL注入、转换异常、内存泄漏等隐患。
  • 调整绩点区间判断为左闭右开写法,解决原代码浮点数边界判断遗漏的问题。

内容的提问来源于stack exchange,提问作者mohammad_soqar101

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:57:20