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

如何优化并简化多条件学生数据查询的逻辑?

学生实体列表查询方法的优化问题

查询逻辑复杂点

条件筛选

根据DivisionId、UserId、SiteId、StudentCategory、Status等参数是否存在进行筛选,部分筛选条件采用OR组合,降低了代码可读性与可维护性。

排序

支持按EnrollmentNumber、StartDate、Status字段进行升序/降序排序,其中EnrollmentNumber需处理如“ABC-123”这类带前缀字符串的自定义比较逻辑。

分页

支持跳过指定数量记录并限制每页结果条数。

复杂关联查询

查询中包含多个Include与ThenInclude调用,用于加载StudentInfo、Statuses、Enrollment等关联数据。

核心顾虑

  • 性能:复杂的筛选与排序可能导致查询速度较慢。
  • 可读性:代码逐渐难以维护与理解。
  • 优化:不确定是否遵循了查询构建与执行的最佳实践。

实现代码

public async Task<Result<List<Student>>> GetAll(GetStudentsQueryParametersDto queryParameters)
{
    try
    {
        IQueryable<Student> query = _dbContext
            .Students.Where(s => s.Enrollment != null && s.Enrollment.IsGroupEnrollment == false)
            .Include(s => s.StudentInfo)
            .Include(s => s.Enrollment);

        if (queryParameters.ClassId.HasValue)
        {
            query = query.Where(s =>
                (s.Enrollment != null && s.Enrollment.ClassId == queryParameters.ClassId)
                || (
                    s.StudentInfo != null
                    && s.StudentInfo.StudentCategory != null
                    && queryParameters.StudentCategories.Contains(s.StudentInfo.StudentCategory)
                )
            );
        }

        if (queryParameters.TeacherId.HasValue)
        {
            query = query.Where(s => s.Enrollment != null && s.Enrollment.TeacherId == queryParameters.TeacherId.Value);
        }

        if (queryParameters.SchoolId.HasValue)
        {
            query = query.Where(s => s.StudentInfo != null && s.StudentInfo.SchoolId == queryParameters.SchoolId.Value);
        }

        if (queryParameters.View != null)
        {
            query = FilterByDate(query, queryParameters.View);
        }

        if (queryParameters.EnrollmentType != null)
        {
            query = FilterByEnrollmentType(query, queryParameters.EnrollmentType);
        }

        query = query
            .Include(s => s.StudentInfo)
            .ThenInclude(i => i!.Statuses)
            .ThenInclude(s => s.StatusReason)
            .Include(s => s.StudentInfo)
            .ThenInclude(i => i!.PreferredSchools)
            .Include(s => s.FocusSubjects);

        if (queryParameters.Status.Count != 0)
        {
            query = FilterByStatus(query, queryParameters.Status);
        }

        var studentsList = new List<Student>();
        if (queryParameters.Sort != null && queryParameters.Sort.ToLower() != "enrollmentnumber")
        {
            var sortedQuery = ApplySorting(query, queryParameters.Sort, queryParameters.Direction);

            var paginatedQuery = sortedQuery.Skip(queryParameters.Offset ?? 0).Take(queryParameters.Limit ?? 10);

            studentsList = await paginatedQuery.ToListAsync();
        }
        else
        {
            studentsList = await query.ToListAsync();
            var orderedStudents = SortByEnrollmentNumber(studentsList, queryParameters.Direction);
            studentsList = orderedStudents.Skip(queryParameters.Offset ?? 0).Take(queryParameters.Limit ?? 10).ToList();
        }

        return Result<List<Student>>.Success(studentsList);
    }
    catch (BadRequestException e)
    {
        _logger.LogError(
            e,
            "Failed to get all students with pagination and parameters: {queryParameters}",
            queryParameters
        );
        return Result<List<Student>>.Failure(Errors.BadRequest(e.Message));
    }
    catch (Exception e)
    {
        _logger.LogError(
            e,
            "Failed to get all students with pagination and parameters: {queryParameters}",
            queryParameters
        );
        return Result<List<Student>>.Failure(Errors.InternalServerError);
    }
}

辅助方法

private static IQueryable<Student> ApplySorting(IQueryable<Student> query, string? sortField, string? sortDirection)
{
    var isDescending = sortDirection?.Equals("desc", StringComparison.OrdinalIgnoreCase) ?? false;

    return sortField?.ToLower() switch
    {
        "startdate" => isDescending
            ? query.OrderByDescending(s => s.StudentInfo!.StartDate)
            : query.OrderBy(s => s.StudentInfo!.StartDate),
        "status" => isDescending
            ? query.OrderByDescending(s =>
                s.StudentInfo!.Statuses.OrderByDescending(st => st.CreatedAt).FirstOrDefault()!.Status
            )
            : query.OrderBy(s =>
                s.StudentInfo!.Statuses.OrderByDescending(st => st.CreatedAt).FirstOrDefault()!.Status
            ),
        _ => isDescending
            ? query.OrderByDescending(s => s.StudentInfo!.StartDate)
            : query.OrderBy(s => s.StudentInfo!.StartDate)
    };
}

private static IOrderedEnumerable<Student> SortByEnrollmentNumber(
    IEnumerable<Student> studentsList,
    string? queryParametersDirection
)
{
    if (!string.IsNullOrEmpty(queryParametersDirection) && queryParametersDirection.ToLower() == "desc")
    {
        return studentsList.OrderByDescending(s => s.Enrollment!.EnrollmentNumber, new EnrollmentNumberComparer());
    }

    return studentsList.OrderBy(s => s.Enrollment!.EnrollmentNumber, new EnrollmentNumberComparer());
}

public class EnrollmentNumberComparer : IComparer<string>
{
    public int Compare(string? enrollmentNumber1, string? enrollmentNumber2)
    {
        ArgumentNullException.ThrowIfNull(enrollmentNumber1);
        ArgumentNullException.ThrowIfNull(enrollmentNumber2);

        if (enrollmentNumber1 == enrollmentNumber2)
            return 0;

        var enrollmentNumber1Parts = enrollmentNumber1.Split('-');
        var enrollmentNumber2Parts = enrollmentNumber2.Split('-');
        var enrollmentNumber1Prefix = enrollmentNumber1Parts[0];
        var enrollmentNumber2Prefix = enrollmentNumber2Parts[0];

        var prefixComparisonResult = enrollmentNumber1Prefix.CompareTo(enrollmentNumber2Prefix);

        if (prefixComparisonResult != 0)
        {
            return prefixComparisonResult;
        }
        else
        {
            return int.Parse(enrollmentNumber1Parts[1]).CompareTo(int.Parse(enrollmentNumber2Parts[1]));
        }
    }
}

内容的提问来源于stack exchange,提问作者Özgür

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:54:53