Verified every finding against the code, then fixed correctness (redirects, advancement reset, duplicate heats, high-jump attempt entry, placement ranking with tie handling, delete-dependency 500s), security (secrets out of config, config-driven admin seed, login lockout, role authorization with school scoping, heat-time IDOR, forwarded headers, last-admin guards), schema integrity (nullable+filtered ExistingStudentId, unique indexes for rounds/heats/bar heights via SchemaIntegrityFixes migration), performance (N+1 removal in high jump/reports/standings/dashboard, SQL-side student paging), and hygiene (duplicate notifications, auto-dismiss scope, local bootstrap-icons, orphaned files, test-data.sql tournament creation). FluentValidation is now registered; AutoMapper bumped to 14.0.0 (advisory fully patched only in licence-changed 15.1.1 — documented). 11 new tests; 63/63 passing. Credential rotation and deploy-time DB_CONNECTION_STRING are required manual follow-ups, documented in bug-fixes.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
33 lines
1.9 KiB
C#
33 lines
1.9 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using SportsDivision.Domain.Entities;
|
|
using SportsDivision.Domain.Interfaces;
|
|
using SportsDivision.Infrastructure.Data;
|
|
|
|
namespace SportsDivision.Infrastructure.Repositories;
|
|
|
|
public class StudentRepository : Repository<Student>, IStudentRepository
|
|
{
|
|
public StudentRepository(ApplicationDbContext context) : base(context) { }
|
|
public async Task<Student?> GetByExistingIdAsync(string existingStudentId) => await _dbSet.Include(s => s.School).FirstOrDefaultAsync(s => s.ExistingStudentId == existingStudentId);
|
|
public async Task<IEnumerable<Student>> GetBySchoolAsync(int schoolId) => await _dbSet.Where(s => s.SchoolId == schoolId).Include(s => s.School).OrderBy(s => s.LastName).ThenBy(s => s.FirstName).ToListAsync();
|
|
public async Task<Student?> GetWithRegistrationsAsync(int studentId) => await _dbSet.Include(s => s.School).Include(s => s.Registrations).ThenInclude(r => r.TournamentEventLevel).ThenInclude(t => t.Event).Include(s => s.Registrations).ThenInclude(r => r.TournamentEventLevel).ThenInclude(t => t.EventLevel).FirstOrDefaultAsync(s => s.StudentId == studentId);
|
|
public async Task<(IReadOnlyList<Student> Items, int TotalCount)> GetPagedAsync(int? schoolId, string? searchTerm, int page, int pageSize)
|
|
{
|
|
IQueryable<Student> query = _dbSet.Include(s => s.School);
|
|
if (schoolId.HasValue)
|
|
query = query.Where(s => s.SchoolId == schoolId.Value);
|
|
if (!string.IsNullOrWhiteSpace(searchTerm))
|
|
query = query.Where(s => s.FirstName.Contains(searchTerm)
|
|
|| s.LastName.Contains(searchTerm)
|
|
|| (s.ExistingStudentId != null && s.ExistingStudentId.Contains(searchTerm)));
|
|
|
|
var total = await query.CountAsync();
|
|
|
|
query = query.OrderBy(s => s.LastName).ThenBy(s => s.FirstName);
|
|
if (pageSize > 0)
|
|
query = query.Skip((page - 1) * pageSize).Take(pageSize);
|
|
|
|
return (await query.ToListAsync(), total);
|
|
}
|
|
}
|