Relational Online Examination and Assessment System
Published Sep 1, 2023
⋅
Updated Dec 20, 2023
⋅
1 minutes read
The Engineering Challenge
Design a web-based testing system with strict ACID guarantees, efficient relational schema for complex question/response joins, and optimized queries to serve concurrent exam sessions with minimal latency.
The Architecture & Tech Stack
- ASP.NET Core web API for backend services.
- Entity Framework Core as ORM with explicit relational modeling.
- Microsoft SQL Server (MSSQL) optimized via indexing and carefully designed foreign-key relationships.
- Role-based authentication and transactional grading.
Core Implementation Logic
// Models/ExamContext.cs
using Microsoft.EntityFrameworkCore;
public class ExamContext : DbContext {
public DbSet<Exam> Exams { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Attempt> Attempts { get; set; }
public DbSet<Answer> Answers { get; set; }
public ExamContext(DbContextOptions<ExamContext> opts) : base(opts) {}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Question>().HasIndex(q => new { q.ExamId, q.Order });
modelBuilder.Entity<Attempt>().HasIndex(a => new { a.UserId, a.ExamId });
// strong FK constraints, cascade rules tuned for consistent deletes
}
}// Controllers/AttemptsController.cs
[ApiController]
[Route("api/[controller]")]
public class AttemptsController : ControllerBase {
private readonly ExamContext _db;
public AttemptsController(ExamContext db) { _db = db; }
[HttpPost("{examId}/submit")]
public async Task<IActionResult> Submit(int examId, SubmissionDto dto) {
using var tx = await _db.Database.BeginTransactionAsync();
var attempt = new Attempt { ExamId = examId, UserId = dto.UserId, StartedAt = dto.StartedAt };
_db.Attempts.Add(attempt);
await _db.SaveChangesAsync();
foreach (var ans in dto.Answers) {
_db.Answers.Add(new Answer { AttemptId = attempt.Id, QuestionId = ans.QuestionId, Selected = ans.Selected });
}
await _db.SaveChangesAsync();
await tx.CommitAsync();
// optimized relational score aggregation
var score = await _db.Answers
.Where(a => a.AttemptId == attempt.Id)
.Join(_db.Questions, a => a.QuestionId, q => q.Id, (a, q) => new { a, q })
.SumAsync(x => x.a.Selected == x.q.CorrectOption ? x.q.Weight : 0);
return Ok(new { attemptId = attempt.Id, score });
}
}System Impact & Results
- Delivered a relational schema with query indexes and transactional submit flow, enabling robust concurrent exam sessions with reliable grading and optimized relational joins.