using Microsoft.EntityFrameworkCore;
using iknow_api.Models;
namespace iknow_api.Data
{
///
/// The schema is owned by sql/ddl.sql, not by EF migrations. Every entity
/// below is mapped explicitly onto the table and column names in that
/// script; nothing here may rely on EF naming conventions.
///
public class AppDbContext : DbContext
{
///
/// The schema created by sql/schema_creation.sql. Every table and enum
/// type is qualified with it, so nothing depends on search_path.
///
public const string ProjectSchema = "project";
public AppDbContext(DbContextOptions options)
: base(options) { }
public DbSet User { get; set; }
public DbSet ContactInfo { get; set; }
public DbSet HighSchool { get; set; }
public DbSet RefreshToken { get; set; }
public DbSet Subjects { get; set; }
public DbSet DependencySubjects { get; set; }
public DbSet Majors { get; set; }
public DbSet PassedSubjects { get; set; }
public DbSet EnrolledSemesters { get; set; }
public DbSet ActiveSemesters { get; set; }
public DbSet SemesterSubjects { get; set; }
public DbSet MajorSubjects { get; set; }
public DbSet UserDocuments { get; set; }
public DbSet Documents { get; set; }
public DbSet Payments { get; set; }
public DbSet ProfessorSubjects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// The enum types themselves are declared in ddl.sql; this tells EF
// they exist so it does not try to treat the columns as text.
modelBuilder.HasPostgresEnum(ProjectSchema, "user_role");
modelBuilder.HasPostgresEnum(ProjectSchema, "hs_type");
modelBuilder.HasPostgresEnum(ProjectSchema, "quota_type");
modelBuilder.HasPostgresEnum(ProjectSchema, "semester_type");
modelBuilder.HasPostgresEnum(ProjectSchema, "grade_type");
// -------------------------
// users
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("users", ProjectSchema);
e.HasKey(u => u.Id);
e.Property(u => u.Id).HasColumnName("id");
e.Property(u => u.Name).HasColumnName("name");
e.Property(u => u.EMBG).HasColumnName("embg");
e.Property(u => u.Surname).HasColumnName("surname");
e.Property(u => u.Index).HasColumnName("index");
e.Property(u => u.Bday).HasColumnName("bday").HasColumnType("timestamp without time zone");
e.Property(u => u.Email).HasColumnName("email");
e.Property(u => u.PasswordHash).HasColumnName("password");
e.Property(u => u.Role).HasColumnName("role");
e.Property(u => u.Quota).HasColumnName("quota");
e.Property(u => u.EnrollmentYear).HasColumnName("enrollment_year");
e.Property(u => u.CreatedAt).HasColumnName("created_at").HasColumnType("timestamp without time zone");
e.HasIndex(u => u.Email).IsUnique();
});
// -------------------------
// high_school
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("high_school", ProjectSchema);
e.HasKey(h => h.Id);
e.Property(h => h.Id).HasColumnName("id");
e.Property(h => h.UserId).HasColumnName("user_id");
e.Property(h => h.GPA).HasColumnName("gpa");
e.Property(h => h.HighSchoolType).HasColumnName("type");
e.HasOne(h => h.User)
.WithOne(u => u.HighSchool)
.HasForeignKey(h => h.UserId)
.OnDelete(DeleteBehavior.Restrict);
});
// -------------------------
// contact
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("contact", ProjectSchema);
e.HasKey(c => c.Id);
e.Property(c => c.Id).HasColumnName("id");
e.Property(c => c.UserId).HasColumnName("user_id");
e.Property(c => c.City).HasColumnName("city");
e.Property(c => c.Municipality).HasColumnName("municipality");
e.Property(c => c.Address).HasColumnName("address");
e.Property(c => c.PhoneNumber).HasColumnName("number");
e.Property(c => c.MicrosoftEmail).HasColumnName("microsoft_email");
e.HasOne(c => c.User)
.WithOne(u => u.ContactInfo)
.HasForeignKey(c => c.UserId)
.OnDelete(DeleteBehavior.Restrict);
});
// -------------------------
// major
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("major", ProjectSchema);
e.HasKey(m => m.Id);
e.Property(m => m.Id).HasColumnName("id");
e.Property(m => m.Name).HasColumnName("name");
});
// -------------------------
// active_semesters
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("active_semesters", ProjectSchema);
e.HasKey(a => a.Id);
e.Property(a => a.Id).HasColumnName("id");
e.Property(a => a.Year).HasColumnName("year");
e.Property(a => a.Type).HasColumnName("type");
});
// -------------------------
// subjects
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("subjects", ProjectSchema);
e.HasKey(s => s.Id);
e.Property(s => s.Id).HasColumnName("id");
e.Property(s => s.Name).HasColumnName("name");
e.Property(s => s.Code).HasColumnName("code");
e.Property(s => s.AwardedCredits).HasColumnName("awarded_credits");
e.Property(s => s.DependencyCredit).HasColumnName("dependency_credit");
});
// -------------------------
// enrolled_semesters
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("enrolled_semesters", ProjectSchema);
e.HasKey(es => es.Id);
e.Property(es => es.Id).HasColumnName("id");
e.Property(es => es.UserId).HasColumnName("user_id");
e.Property(es => es.QuotaType).HasColumnName("quota");
e.Property(es => es.MajorId).HasColumnName("major_id");
e.Property(es => es.Note).HasColumnName("note");
e.Property(es => es.StudentComment).HasColumnName("student_comment");
e.Property(es => es.CratedAt).HasColumnName("created_at").HasColumnType("timestamp without time zone");
e.Property(es => es.LastChange).HasColumnName("last_change").HasColumnType("timestamp without time zone");
e.Property(es => es.Verified).HasColumnName("completed").HasColumnType("timestamp without time zone");
e.Property(es => es.SemesterId).HasColumnName("semester_id");
e.HasOne(es => es.User)
.WithMany(u => u.Enrolments)
.HasForeignKey(es => es.UserId)
.OnDelete(DeleteBehavior.Restrict);
// Note: this deliberately keys off SemesterId. An earlier version
// pointed the foreign key at es.Id, which silently tied every
// enrolment to the semester that happened to share its id.
e.HasOne(es => es.Semester)
.WithMany(a => a.EnrolledSemesters)
.HasForeignKey(es => es.SemesterId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(es => es.Major)
.WithMany()
.HasForeignKey(es => es.MajorId)
.OnDelete(DeleteBehavior.Restrict);
});
// -------------------------
// semesters_subjects
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("semesters_subjects", ProjectSchema);
e.HasKey(ss => ss.Id);
e.Property(ss => ss.Id).HasColumnName("id");
e.Property(ss => ss.EnrolledSemesterId).HasColumnName("enrolled_semesters_id");
e.Property(ss => ss.SubjectId).HasColumnName("subjects_id");
e.Property(ss => ss.ProfessorId).HasColumnName("professor_id");
e.Property(ss => ss.Signature).HasColumnName("signature");
e.HasOne(ss => ss.Professor)
.WithMany(u => u.TeachingSubjects)
.HasForeignKey(ss => ss.ProfessorId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(ss => ss.Subject)
.WithMany(s => s.SemesterSubjects)
.HasForeignKey(ss => ss.SubjectId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(ss => ss.EnrolledSemester)
.WithMany(es => es.SemesterSubjects)
.HasForeignKey(ss => ss.EnrolledSemesterId)
.OnDelete(DeleteBehavior.Restrict);
});
// -------------------------
// passed_subjects
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("passed_subjects", ProjectSchema);
e.HasKey(ps => ps.Id);
e.Property(ps => ps.Id).HasColumnName("id");
e.Property(ps => ps.SemesterSubjectId).HasColumnName("enrolled_id");
e.Property(ps => ps.Grade).HasColumnName("grade");
e.Property(ps => ps.DatePassed).HasColumnName("date_passed").HasColumnType("timestamp without time zone");
e.HasOne(ps => ps.SemesterSubject)
.WithOne(ss => ss.PassedSubject)
.HasForeignKey(ps => ps.SemesterSubjectId)
.OnDelete(DeleteBehavior.Cascade);
});
// -------------------------
// documents
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("documents", ProjectSchema);
e.HasKey(d => d.Id);
e.Property(d => d.Id).HasColumnName("id");
e.Property(d => d.Name).HasColumnName("type");
e.Property(d => d.Body).HasColumnName("body");
e.Property(d => d.Cost).HasColumnName("cost");
});
// -------------------------
// user_documents
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("user_documents", ProjectSchema);
e.HasKey(ud => new { ud.UserId, ud.DocumentId });
e.Property(ud => ud.UserId).HasColumnName("user_id");
e.Property(ud => ud.DocumentId).HasColumnName("document_id");
e.HasOne(ud => ud.User)
.WithMany(u => u.Documents)
.HasForeignKey(ud => ud.UserId)
.OnDelete(DeleteBehavior.Cascade);
e.HasOne(ud => ud.Documents)
.WithMany(d => d.User)
.HasForeignKey(ud => ud.DocumentId)
.OnDelete(DeleteBehavior.Cascade);
});
// -------------------------
// token
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("token", ProjectSchema);
e.HasKey(t => t.Id);
e.Property(t => t.Id).HasColumnName("id");
e.Property(t => t.UserId).HasColumnName("user_id");
e.Property(t => t.Token).HasColumnName("token");
e.Property(t => t.ExpiresAt).HasColumnName("expires_at").HasColumnType("timestamp without time zone");
e.Property(t => t.IsValid).HasColumnName("is_valid");
e.HasOne(t => t.User)
.WithMany()
.HasForeignKey(t => t.UserId)
.OnDelete(DeleteBehavior.Cascade);
});
// -------------------------
// payment
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("payment", ProjectSchema);
e.HasKey(p => p.Id);
e.Property(p => p.Id).HasColumnName("id");
e.Property(p => p.EnrollmentInfoId).HasColumnName("enrollment_id");
e.Property(p => p.Amount).HasColumnName("amount");
// No user_id: the student comes from the enrolment. Removing it
// was the BCNF fix from Phase 5 (enrolled_id -> user_id).
e.HasOne(p => p.EnrolledSemesters)
.WithMany(es => es.Users)
.HasForeignKey(p => p.EnrollmentInfoId)
.OnDelete(DeleteBehavior.Cascade);
});
// -------------------------
// major_subjects
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("major_subjects", ProjectSchema);
e.HasKey(ms => new { ms.MajorId, ms.SubjectId });
e.Property(ms => ms.MajorId).HasColumnName("major_id");
e.Property(ms => ms.SubjectId).HasColumnName("subject_id");
e.Property(ms => ms.MandatorySemester).HasColumnName("mandatory_semester");
e.HasOne(ms => ms.Majors)
.WithMany(m => m.Subjects)
.HasForeignKey(ms => ms.MajorId)
.OnDelete(DeleteBehavior.Cascade);
e.HasOne(ms => ms.Subjects)
.WithMany(s => s.Majors)
.HasForeignKey(ms => ms.SubjectId)
.OnDelete(DeleteBehavior.Cascade);
});
// -------------------------
// dependency_subject
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("dependency_subject", ProjectSchema);
e.HasKey(ds => new { ds.SubjectId, ds.DependencyId });
e.Property(ds => ds.SubjectId).HasColumnName("subject_id");
e.Property(ds => ds.DependencyId).HasColumnName("dependency_id");
e.HasOne(ds => ds.Subject)
.WithMany(s => s.Dependencies)
.HasForeignKey(ds => ds.SubjectId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(ds => ds.Dependency)
.WithMany(s => s.Dependents)
.HasForeignKey(ds => ds.DependencyId)
.OnDelete(DeleteBehavior.Restrict);
});
// -------------------------
// professour_subjects (spelling matches the table in ddl.sql)
// -------------------------
modelBuilder.Entity(e =>
{
e.ToTable("professour_subjects", ProjectSchema);
e.HasKey(ps => new { ps.ProfessorId, ps.SemesterId, ps.SubjectId });
e.Property(ps => ps.ProfessorId).HasColumnName("prof_id");
e.Property(ps => ps.SemesterId).HasColumnName("active_semester_id");
e.Property(ps => ps.SubjectId).HasColumnName("subject_id");
e.HasOne(ps => ps.Professor)
.WithMany()
.HasForeignKey(ps => ps.ProfessorId)
.OnDelete(DeleteBehavior.Cascade);
e.HasOne(ps => ps.Semester)
.WithMany()
.HasForeignKey(ps => ps.SemesterId)
.OnDelete(DeleteBehavior.Cascade);
e.HasOne(ps => ps.Subject)
.WithMany()
.HasForeignKey(ps => ps.SubjectId)
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}