添加项目文件。

This commit is contained in:
SpecialX
2025-05-23 19:03:00 +08:00
parent 6fa7679fd3
commit d36fef2bbb
185 changed files with 13413 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class AssignmentAttachmentConfiguration : IEntityTypeConfiguration<AssignmentAttachment>
{
public void Configure(EntityTypeBuilder<AssignmentAttachment> builder)
{
builder.ToTable("assignment_attachments");
builder.HasKey(aa => aa.Id);
builder.Property(aa => aa.Id)
.HasColumnName("id");
builder.Property(aa => aa.AssignmentId)
.HasColumnName("assignment_id")
.IsRequired();
builder.Property(aa => aa.FilePath)
.HasColumnName("file_path")
.IsRequired()
.HasMaxLength(255);
builder.Property(aa => aa.FileName)
.HasColumnName("file_name")
.IsRequired()
.HasMaxLength(255);
builder.Property(aa => aa.UploadedAt)
.HasColumnName("uploaded_at")
.ValueGeneratedOnAdd(); // Set value on creation
builder.Property(aa => aa.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false);
// Configure the relationship explicitly
builder.HasOne(aa => aa.Assignment) // An AssignmentAttachment has one Assignment
.WithMany(a => a.AssignmentAttachments) // An Assignment has many AssignmentAttachments (assuming 'Attachments' collection in Assignment)
.HasForeignKey(aa => aa.AssignmentId) // The foreign key is AssignmentAttachment.AssignmentId
.IsRequired() // It's a required relationship based on your [Required] attribute
.OnDelete(DeleteBehavior.Cascade); // If an Assignment is deleted, its attachments should also be deleted
}
}
}

View File

@@ -0,0 +1,50 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class AssignmentClassConfiguration : IEntityTypeConfiguration<AssignmentClass>
{
public void Configure(EntityTypeBuilder<AssignmentClass> builder)
{
// 设置表名为 "assignment_class"
builder.ToTable("assignment_class");
// 设置复合主键
builder.HasKey(ac => new { ac.AssignmentId, ac.ClassId });
// 配置 AssignmentId 列名
builder.Property(ac => ac.AssignmentId)
.HasColumnName("assignment_id");
// 配置 ClassId 列名
builder.Property(ac => ac.ClassId)
.HasColumnName("class_id");
// 配置 AssignedAt 列名
builder.Property(ac => ac.AssignedAt)
.HasColumnName("assigned_at")
.IsRequired(); // 通常时间字段不能为空
// 配置 IsDeleted 列名
builder.Property(ac => ac.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 可以设置默认值,表示默认未删除
// 配置到 Assignment 的关系 (多对一)
// 假设 Assignment 类中有一个名为 AssignmentClasses 的集合属性
builder.HasOne(ac => ac.Assignment) // AssignmentClass 有一个 Assignment
.WithMany(a => a.AssignmentClasses) // Assignment 有多个 AssignmentClass 记录
.HasForeignKey(ac => ac.AssignmentId) // 通过 AssignmentId 建立外键
.OnDelete(DeleteBehavior.Cascade); // 当 Assignment 被删除时,相关的 AssignmentClass 记录也级联删除
// 配置到 Class 的关系 (多对一)
// 假设 Class 类中有一个名为 AssignmentClasses 的集合属性
builder.HasOne(ac => ac.Class) // AssignmentClass 有一个 Class
.WithMany(c => c.AssignmentClasses) // Class 有多个 AssignmentClass 记录
.HasForeignKey(ac => ac.ClassId) // 通过 ClassId 建立外键
.OnDelete(DeleteBehavior.Cascade); // 当 Class 被删除时,相关的 AssignmentClass 记录也级联删除
}
}
}

View File

@@ -0,0 +1,82 @@
using Entities.Contracts;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace TechHelper.Context.Configuration
{
public class AssignmentConfiguration : IEntityTypeConfiguration<Assignment>
{
public void Configure(EntityTypeBuilder<Assignment> builder)
{
builder.ToTable("assignments");
builder.HasKey(a => a.Id);
builder.Property(a => a.Id)
.HasColumnName("id")
.ValueGeneratedOnAdd();
builder.Property(a => a.Title)
.IsRequired()
.HasColumnName("title")
.HasMaxLength(255);
builder.Property(a => a.Description)
.HasColumnName("description");
builder.Property(a => a.DueDate)
.IsRequired()
.HasColumnName("due_date");
builder.Property(a => a.TotalPoints)
.HasColumnName("total_points");
builder.Property(a => a.CreatedBy)
.HasColumnName("created_by");
builder.Property(a => a.CreatedAt)
.IsRequired()
.HasColumnName("created_at");
builder.Property(a => a.UpdatedAt)
.IsRequired()
.HasColumnName("updated_at");
builder.Property(a => a.IsDeleted)
.HasColumnName("deleted");
// 配置导航属性和关系
// 关系: Assignment (多) 到 User (一) - CreatedBy 外键
// 假设 User 实体有 Id 作为主键
// .WithMany() 表示 User 有多个 Assignment但这里不指定 User 上的导航属性名称
// 如果 User 有一个名为 AssignmentsCreated 的导航属性,应写为 .WithMany(u => u.AssignmentsCreated)
builder.HasOne(a => a.Creator)
.WithMany() // User 实体没有指向 Assignment 的导航属性 (或我们不知道)
.HasForeignKey(a => a.CreatedBy)
.IsRequired(); // CreatedBy 是必填的,对应 [Required] 在外键属性上
// 关系: Assignment (一) 到 AssignmentClass (多)
// 假设 AssignmentClass 实体包含一个名为 AssignmentId 的外键属性
builder.HasMany(a => a.AssignmentClasses)
.WithOne(ac => ac.Assignment) // AssignmentClass 没有指向 Assignment 的导航属性 (或我们不知道)
.HasForeignKey("AssignmentId") // 指定外键名称为 AssignmentId
.OnDelete(DeleteBehavior.Cascade); // 如果 Assignment 被删除,关联的 AssignmentClass 也会被删除
// 关系: Assignment (一) 到 AssignmentAttachment (多)
// 假设 AssignmentAttachment 实体包含一个名为 AssignmentId 的外键属性
builder.HasMany(a => a.AssignmentAttachments)
.WithOne(aa => aa.Assignment)
.HasForeignKey("AssignmentId")
.OnDelete(DeleteBehavior.Cascade);
// 关系: Assignment (一) 到 Submission (多)
// 假设 Submission 实体包含一个名为 AssignmentId 的外键属性
builder.HasMany(a => a.Submissions)
.WithOne(s => s.Assignment)
.HasForeignKey("AssignmentId")
.OnDelete(DeleteBehavior.Cascade);
}
}
}

View File

@@ -0,0 +1,83 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class AssignmentGroupConfiguration : IEntityTypeConfiguration<AssignmentGroup>
{
public void Configure(EntityTypeBuilder<AssignmentGroup> builder)
{
// 1. 设置表名
// 将此实体映射到数据库中名为 "assignment_detail" 的表。
builder.ToTable("assignment_group");
// 2. 配置主键
// Id 属性作为主键。
builder.HasKey(ag => ag.Id);
// 3. 配置列名、必需性、长度和默认值
// 配置 Id 属性对应的数据库列名为 "id"。
builder.Property(ag => ag.Id)
.HasColumnName("id");
// EF Core 默认 Guid 类型主键由应用程序生成,因此无需 ValueGeneratedOnAdd()。
// 配置 AssignmentId 属性对应的数据库列名为 "assignment",并设置为必需字段。
builder.Property(ag => ag.AssignmentId)
.HasColumnName("assignment")
.IsRequired();
// 配置 Title 属性对应的数据库列名为 "title",设置为必需字段,并设置最大长度。
builder.Property(ag => ag.Title)
.HasColumnName("title")
.IsRequired()
.HasMaxLength(65535); // 对应 MaxLength(65535)
// 配置 Descript 属性对应的数据库列名为 "descript",并设置最大长度。
builder.Property(ag => ag.Descript)
.HasColumnName("descript")
.HasMaxLength(65535); // 对应 MaxLength(65535)
// 配置 TotalPoints 属性对应的数据库列名为 "total_points"。
// TotalPoints 是 decimal? 类型,默认就是可选的,无需 IsRequired(false)。
builder.Property(ag => ag.TotalPoints)
.HasColumnName("total_points");
// 配置 Number 属性对应的数据库列名为 "number"。
builder.Property(ag => ag.Number)
.HasColumnName("number")
.IsRequired(); // byte 默认非空,显式 IsRequired 增加可读性。
// 配置 ParentGroup 属性对应的数据库列名为 "sub_group"。
// ParentGroup 是 Guid? 类型,默认就是可选的,无需 IsRequired(false)。
builder.Property(ag => ag.ParentGroup)
.HasColumnName("sub_group");
// 配置 IsDeleted 属性对应的数据库列名为 "deleted",并设置默认值为 false。
builder.Property(ag => ag.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 适用于软删除策略
// 4. 配置导航属性和外键关系
// 配置 AssignmentGroup 到 Assignment 的多对一关系。
// 一个 AssignmentGroup 记录属于一个 Assignment。
builder.HasOne(ag => ag.Assignment) // 当前 AssignmentGroup 有一个 Assignment
.WithMany(a => a.AssignmentGroups) // 该 Assignment 可以有多个 AssignmentGroup 记录
.HasForeignKey(ag => ag.AssignmentId) // 通过 AssignmentId 建立外键
.OnDelete(DeleteBehavior.Cascade); // 当关联的 Assignment 被删除时,其所有相关的 AssignmentGroup 记录也级联删除。
// 配置 AssignmentGroup 到 AssignmentGroup 的自引用关系(父子关系)。
// 一个 AssignmentGroup 可以有一个父 AssignmentGroup (SubAssignmentGroup)。
// 假设父 AssignmentGroup 实体中有一个名为 ChildAssignmentGroups 的集合属性来表示它所包含的所有子组。
builder.HasOne(ag => ag.ParentAssignmentGroup) // 当前 AssignmentGroup 有一个父 AssignmentGroup
.WithMany(parentAg => parentAg.ChildAssignmentGroups) // 该父 AssignmentGroup 可以有多个子 AssignmentGroup
.HasForeignKey(ag => ag.ParentGroup) // 通过 SubGroup 建立外键
.IsRequired(false) // SubGroup 是可空的 (Guid?),所以这个关系是可选的。
.OnDelete(DeleteBehavior.SetNull); // 当父 AssignmentGroup 被删除时,其子 AssignmentGroup 的 SubGroup 外键将被设置为 NULL。
// 如果你希望父组被删除时子组不能脱离父组(即不允许父组被删除),
// 可以使用 DeleteBehavior.Restrict 或 DeleteBehavior.NoAction。
}
}
}

View File

@@ -0,0 +1,86 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class AssignmentQuestionConfiguration : IEntityTypeConfiguration<AssignmentQuestion>
{
public void Configure(EntityTypeBuilder<AssignmentQuestion> builder)
{
// 1. 设置表名
builder.ToTable("assignment_questions");
// 2. 设置主键
builder.HasKey(aq => aq.Id);
// 3. 配置列名、必需性及其他属性
// 配置 Id 列
builder.Property(aq => aq.Id)
.HasColumnName("id");
// 配置 QuestionId 列 (已修正拼写)
builder.Property(aq => aq.QuestionId)
.HasColumnName("question_id")
.IsRequired();
// 配置 QuestionNumber 列
builder.Property(aq => aq.QuestionNumber)
.HasColumnName("question_number")
.IsRequired(); // uint 类型默认非空
// 配置 CreatedAt 列
builder.Property(aq => aq.CreatedAt)
.HasColumnName("created_at")
.IsRequired(); // 通常创建时间字段是非空的
// 配置 AssignmentGroupId 列
// 该列在数据库中名为 "detail_id"
builder.Property(aq => aq.AssignmentGroupId)
.HasColumnName("detail_id")
.IsRequired();
// 配置 IsDeleted 列
builder.Property(aq => aq.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 适用于软删除策略
// 4. 配置导航属性和外键关系
// ---
// 配置 AssignmentQuestion 到 Question 的关系 (多对一)
// 一个 AssignmentQuestion 属于一个 Question。
//
// 假设 `Question` 实体中有一个名为 `AssignmentQuestions` 的 `ICollection<AssignmentQuestion>` 集合属性。
builder.HasOne(aq => aq.Question) // 当前 AssignmentQuestion 有一个 Question
.WithMany(q => q.AssignmentQuestions) // 那个 Question 可以有多个 AssignmentQuestion
.HasForeignKey(aq => aq.QuestionId) // 外键是 AssignmentQuestion.QuestionId
.OnDelete(DeleteBehavior.Cascade); // 当 Question 被删除时,相关的 AssignmentQuestion 也级联删除。
// ---
// 配置 AssignmentQuestion 到 AssignmentGroup 的关系 (多对一)
// 一个 AssignmentQuestion 属于一个 AssignmentGroup。
//
// 你的 `AssignmentQuestion` 类现在有了 `public AssignmentGroup AssignmentGroup { get; set; }`
// 这是一个非常好的改进,它与 `AssignmentGroupId` 外键完美匹配。
// 假设 `AssignmentGroup` 实体中有一个名为 `AssignmentQuestions` 的 `ICollection<AssignmentQuestion>` 集合属性。
builder.HasOne(aq => aq.AssignmentGroup) // 当前 AssignmentQuestion 有一个 AssignmentGroup
.WithMany(ag => ag.AssignmentQuestions) // 那个 AssignmentGroup 可以有多个 AssignmentQuestion
.HasForeignKey(aq => aq.AssignmentGroupId) // 外键是 AssignmentQuestion.AssignmentGroupId (列名 detail_id)
.OnDelete(DeleteBehavior.Cascade); // 当 AssignmentGroup 被删除时,相关的 AssignmentQuestion 也级联删除。
// ---
// 配置 AssignmentQuestion 到 SubmissionDetail 的关系 (一对多)
// 一个 AssignmentQuestion 可以有多个 SubmissionDetail。
//
// 这个关系通常从 "多" 的一方(`SubmissionDetail` 实体)来配置外键。
// 假设 `SubmissionDetail` 实体有一个 `AssignmentQuestionId` 外键和 `AssignmentQuestion` 导航属性。
builder.HasMany(aq => aq.SubmissionDetails) // 当前 AssignmentQuestion 有多个 SubmissionDetail
.WithOne(sd => sd.AssignmentQuestion); // 每一个 SubmissionDetail 都有一个 AssignmentQuestion
// .HasForeignKey(sd => sd.AssignmentQuestionId); // 外键的配置应在 `SubmissionDetailConfiguration` 中进行
}
}
}

View File

@@ -0,0 +1,99 @@
using Entities.Contracts;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace TechHelper.Context.Configuration
{
public class ClassConfiguration : IEntityTypeConfiguration<Class>
{
public void Configure(EntityTypeBuilder<Class> builder)
{
// 1. 表名映射
builder.ToTable("classes");
// 2. 主键
builder.HasKey(c => c.Id);
builder.Property(c => c.Id)
.HasColumnName("id");
builder.Property(c => c.Grade)
.HasColumnName("grade");
// 3. Name 属性
builder.Property(c => c.Number)
.HasColumnName("class");
// 4. Description 属性
builder.Property(c => c.Description)
.HasColumnName("description")
.IsRequired(false); // 明确其为可选,尽管 string 类型默认是可空的。
// 5. TeacherId (外键)
builder.Property(c => c.HeadTeacherId)
.HasColumnName("head_teacher_id")
.IsRequired(false);
// 6. CreatedAt 属性
builder.Property(c => c.CreatedAt)
.HasColumnName("created_at")
.IsRequired()
.ValueGeneratedOnAdd();
// DateTime 是非可空类型,因此按类型是必需的。
// 如果希望数据库在创建时设置默认值,可以考虑 .HasDefaultValueSql("GETUTCDATE()") (SQL Server) 或类似方法。
// 7. UpdatedAt 属性
builder.Property(c => c.UpdatedAt)
.HasColumnName("updated_at")
.IsRequired()
.ValueGeneratedOnAddOrUpdate(); // DateTime 是非可空类型。
// 如果需要在修改时自动更新,可以考虑配置(例如通过拦截器或触发器)。
// 8. IsDeleted 属性 (用于软删除)
builder.Property(c => c.IsDeleted)
.HasColumnName("deleted")
.IsRequired()
.HasDefaultValue(false); // 通常默认为未删除。
// --- 导航属性配置 ---
// 9. 与 User (Teacher) 的关系
builder.Property(c => c.HeadTeacherId).HasColumnName("head_teacher_id");
builder.HasOne(c => c.HeadTeacher) // Class 实体中的导航属性
.WithMany() // User 实体中可以不定义反向导航到其作为班主任的班级集合
// 如果 User 实体中有 public ICollection<Class> HeadManagedClasses { get; set; }
// 则这里应为 .WithMany(u => u.HeadManagedClasses)
.HasForeignKey(c => c.HeadTeacherId) // Class 实体中的外键
.IsRequired() // 班主任是必需的
.OnDelete(DeleteBehavior.Restrict); // 删除用户时,如果其是班主任,则限制删除
// --- 授课老师关系 (多对多) ---
// 这个关系主要通过 ClassTeacherConfiguration 来定义其两端。
// Class 到 ClassTeacher 是一对多。
builder.HasMany(c => c.ClassTeachers) // Class 实体中的集合导航属性
.WithOne(ct => ct.Class) // ClassTeacher 实体中指向 Class 的导航属性
.HasForeignKey(ct => ct.ClassId) // ClassTeacher 实体中的外键
.OnDelete(DeleteBehavior.Cascade); // 如果班级被删除,相关的教师关联也应删除
// 10. 与 ClassStudent 的关系 (多对多中间表或一对多)
// 一对多:一个 Class 有多个 ClassStudent。一个 ClassStudent 属于一个 Class。
// 假设 ClassStudent 有 'ClassId' 外键和 'Class' 导航属性。
builder.HasMany(c => c.ClassStudents) // Class 中的集合导航属性
.WithOne(cs => cs.Class) // 假设 ClassStudent 中有 'public Class Class { get; set; }'
.HasForeignKey(cs => cs.ClassId) // 假设 ClassStudent 中有 'public Guid ClassId { get; set; }'
.OnDelete(DeleteBehavior.Cascade); // 常见:如果一个班级被删除,其学生注册记录也应被删除。
// 11. 与 AssignmentClass 的关系 (多对多中间表或一对多)
// 一对多:一个 Class 有多个 AssignmentClass。一个 AssignmentClass 属于一个 Class。
// 假设 AssignmentClass 有 'ClassId' 外键和 'Class' 导航属性。
builder.HasMany(c => c.AssignmentClasses) // Class 中的集合导航属性
.WithOne(ac => ac.Class) // 假设 AssignmentClass 中有 'public Class Class { get; set; }'
.HasForeignKey(ac => ac.ClassId) // 假设 AssignmentClass 中有 'public Guid ClassId { get; set; }'
.OnDelete(DeleteBehavior.Cascade); // 常见:如果一个班级被删除,其作业关联也应被删除。
// --- 可选:索引 ---
// builder.HasIndex(c => c.TeacherId).HasDatabaseName("IX_classes_teacher_id");
// builder.HasIndex(c => c.Name).IsUnique(false).HasDatabaseName("IX_classes_name"); // 如果经常按名称搜索
}
}
}

View File

@@ -0,0 +1,64 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class ClassStudentConfiguration : IEntityTypeConfiguration<ClassStudent>
{
public void Configure(EntityTypeBuilder<ClassStudent> builder)
{
// 1. 设置表名
// 将此实体映射到数据库中名为 "class_student" 的表。
builder.ToTable("class_student");
// 2. 设置复合主键
// ClassId 和 StudentId 的组合作为主键,确保一个班级中一个学生只有一条入学记录。
builder.HasKey(cs => new { cs.ClassId, cs.StudentId });
// 3. 配置列名和属性特性
// 配置 ClassId 属性对应的数据库列名为 "class_id"。
builder.Property(cs => cs.ClassId)
.HasColumnName("class_id");
// 配置 StudentId 属性对应的数据库列名为 "student_id"。
builder.Property(cs => cs.StudentId)
.HasColumnName("student_id");
// 配置 EnrollmentDate 属性对应的数据库列名为 "enrollment_date",并设置为必需字段。
builder.Property(cs => cs.EnrollmentDate)
.HasColumnName("enrollment_date")
.IsRequired();
// 配置 IsDeleted 属性对应的数据库列名为 "deleted",并设置默认值为 false。
builder.Property(cs => cs.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 常用作软删除标记
// 4. 配置导航属性和外键关系
// ---
// 配置 ClassStudent 到 Class 的关系 (多对一)
// 一个 ClassStudent 联结记录属于一个 Class。
//
// 假设 `Class` 实体中有一个名为 `ClassStudents` 的 `ICollection<ClassStudent>` 集合属性。
builder.HasOne(cs => cs.Class) // 当前 ClassStudent 链接到一个 Class
.WithMany(c => c.ClassStudents) // 那个 Class 可以有多个 ClassStudent 记录
.HasForeignKey(cs => cs.ClassId) // 外键是 ClassStudent.ClassId
.OnDelete(DeleteBehavior.Cascade); // 当 Class 被删除时,相关的 ClassStudent 记录也级联删除。
// ---
// 配置 ClassStudent 到 User (Student) 的关系 (多对一)
// 一个 ClassStudent 联结记录属于一个 User (作为 Student)。
//
// 假设 `User` 实体中有一个名为 `EnrolledClassesLink` 的 `ICollection<ClassStudent>` 集合属性,
// 用于表示该用户所注册的班级联结记录 (与 `ClassTeacherConfiguration` 中的模式类似)。
builder.HasOne(cs => cs.Student) // 当前 ClassStudent 链接到一个 User (学生)
.WithMany(u => u.EnrolledClassesLink) // 那个 User (学生) 可以有多个 ClassStudent 记录
.HasForeignKey(cs => cs.StudentId) // 外键是 ClassStudent.StudentId
.OnDelete(DeleteBehavior.Restrict); // 当 User (学生) 被删除时,如果还有相关的 ClassStudent 记录,则会阻止删除。
// 这是更安全的做法,以避免意外数据丢失。如果你希望学生被删除时,其所有注册关系也一并删除,可改为 DeleteBehavior.Cascade。
}
}
}

View File

@@ -0,0 +1,61 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class ClassTeacherConfiguration : IEntityTypeConfiguration<ClassTeacher>
{
public void Configure(EntityTypeBuilder<ClassTeacher> builder)
{
// 1. 设置表名
// 将此实体映射到数据库中名为 "class_teachers" 的表。
builder.ToTable("class_teachers");
// 2. 设置复合主键
// ClassId 和 TeacherId 的组合作为主键,确保一个班级中一个老师只有一条任教记录。
// 这要求 ClassTeacher 类中的 ClassId 和 TeacherId 属性上都添加了 [Key] 特性。
builder.HasKey(ct => new { ct.ClassId, ct.TeacherId });
// 3. 配置列名和属性特性
// 配置 ClassId 属性对应的数据库列名为 "class_id"。
builder.Property(ct => ct.ClassId)
.HasColumnName("class_id");
// 配置 TeacherId 属性对应的数据库列名为 "teacher_id"。
builder.Property(ct => ct.TeacherId)
.HasColumnName("teacher_id");
// 配置 SubjectTaught 属性对应的数据库列名为 "subject_taught"。
// 假设 SubjectTaught 可以为空,所以没有 .IsRequired()
builder.Property(ct => ct.SubjectTaught)
.HasColumnName("subject_taught");
// 4. 配置导航属性和外键关系
// ---
// 配置 ClassTeacher 到 Class 的关系 (多对一)
// 一个 ClassTeacher 联结记录属于一个 Class。
//
// 假设 `Class` 实体中有一个名为 `ClassTeachers` 的 `ICollection<ClassTeacher>` 集合属性。
builder.HasOne(ct => ct.Class) // 当前 ClassTeacher 链接到一个 Class
.WithMany(c => c.ClassTeachers) // 那个 Class 可以有多个 ClassTeacher 记录
.HasForeignKey(ct => ct.ClassId) // 外键是 ClassTeacher.ClassId
.OnDelete(DeleteBehavior.Cascade); // 当 Class 被删除时,相关的 ClassTeacher 记录也级联删除。
// ---
// 配置 ClassTeacher 到 User (Teacher) 的关系 (多对一)
// 一个 ClassTeacher 联结记录属于一个 User (作为 Teacher)。
//
// 假设 `User` 实体中有一个名为 `TaughtClassesLink` 的 `ICollection<ClassTeacher>` 集合属性,
// 用于表示该用户所教授的班级联结记录。
builder.HasOne(ct => ct.Teacher) // 当前 ClassTeacher 链接到一个 User (老师)
.WithMany(u => u.TaughtClassesLink) // 那个 User (老师) 可以有多个 ClassTeacher 记录 (为所教授的班级)
.HasForeignKey(ct => ct.TeacherId) // 外键是 ClassTeacher.TeacherId
.OnDelete(DeleteBehavior.Restrict); // 当 User (老师) 被删除时,如果还有相关的 ClassTeacher 记录,则会阻止删除。
// 这通常是防止数据丢失的更安全选择。如果你希望老师被删除时,其所有任教关系也一并删除,可改为 DeleteBehavior.Cascade。
}
}
}

View File

@@ -0,0 +1,102 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class QuestionConfiguration : IEntityTypeConfiguration<Question>
{
public void Configure(EntityTypeBuilder<Question> builder)
{
// 1. 设置表名
builder.ToTable("questions");
// 2. 设置主键
builder.HasKey(q => q.Id);
// 3. 配置列名、必需性、长度及其他属性
// Id
builder.Property(q => q.Id)
.HasColumnName("id");
// 对于 Guid 类型的主键EF Core 默认由应用程序生成值,无需 ValueGeneratedOnAdd()
// QuestionText
builder.Property(q => q.QuestionText)
.HasColumnName("question_text")
.IsRequired()
.HasMaxLength(65535); // 对应 MaxLength(65535)
// QuestionType (枚举作为字符串存储)
builder.Property(q => q.QuestionType)
.HasColumnName("question_type")
.IsRequired()
.HasConversion<string>() // <-- 重要:将枚举存储为字符串
.HasMaxLength(20); // <-- 应用最大长度
// CorrectAnswer
builder.Property(q => q.CorrectAnswer)
.HasColumnName("correct_answer")
.HasMaxLength(65535); // 对应 MaxLength(65535)
// DifficultyLevel (枚举作为字符串存储)
builder.Property(q => q.DifficultyLevel)
.HasColumnName("difficulty_level")
.HasConversion<string>() // <-- 重要:将枚举存储为字符串
.HasMaxLength(10); // <-- 应用最大长度
// DifficultyLevel 属性没有 [Required],所以默认是可选的
// SubjectArea
builder.Property(q => q.SubjectArea)
.HasColumnName("subject_area")
.HasConversion<string>() // <--- 重要:将枚举存储为字符串
.HasMaxLength(100); // <--- 应用最大长度 (从原 StringLength 继承)
// CreatedBy
builder.Property(q => q.CreatedBy)
.HasColumnName("created_by")
.IsRequired();
// CreatedAt (自动在添加时设置)
builder.Property(q => q.CreatedAt)
.HasColumnName("created_at")
.IsRequired() // 通常创建时间是必需的
.ValueGeneratedOnAdd(); // EF Core 在实体添加时自动设置值
// UpdatedAt (自动在添加或更新时设置,并作为并发令牌)
builder.Property(q => q.UpdatedAt)
.HasColumnName("updated_at")
.IsRequired() // 通常更新时间是必需的
.ValueGeneratedOnAddOrUpdate() // EF Core 在实体添加或更新时自动设置值
.IsConcurrencyToken(); // 用作乐观并发令牌,防止数据冲突
// IsDeleted (软删除标记,默认 false)
builder.Property(q => q.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false);
// 4. 配置导航属性和外键关系
// ---
// 配置 Question 到 User (Creator) 的关系 (多对一)
// 一个 Question 由一个 User (Creator) 创建。
//
// 假设 `User` 实体中有一个名为 `CreatedQuestions` 的 `ICollection<Question>` 集合属性。
builder.HasOne(q => q.Creator) // 当前 Question 有一个 Creator
.WithMany(u => u.CreatedQuestions) // 那个 Creator 可以创建多个 Question
.HasForeignKey(q => q.CreatedBy) // 外键是 Question.CreatedBy
.OnDelete(DeleteBehavior.Restrict); // 当 User (Creator) 被删除时,如果还有他/她创建的 Question则会阻止删除。
// 由于 CreatedBy 是 [Required]Prevent/Restrict 是一个安全的选择。
// ---
// 配置 Question 到 AssignmentQuestion 的关系 (一对多)
// 一个 Question 可以被多个 AssignmentQuestion 引用。
//
// 这个关系的外键配置通常在 "多" 的一方(`AssignmentQuestion` 实体)进行。
// 假设 `AssignmentQuestion` 实体有一个 `QuestionId` 外键和 `Question` 导航属性。
builder.HasMany(q => q.AssignmentQuestions) // 当前 Question 有多个 AssignmentQuestion
.WithOne(aq => aq.Question); // 每一个 AssignmentQuestion 都有一个 Question
// .HasForeignKey(aq => aq.QuestionId); // 外键的配置应在 `AssignmentQuestionConfiguration` 中进行
}
}
}

View File

@@ -0,0 +1,25 @@
using Entities.Contracts;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace TechHelper.Context.Configuration
{
public class RoleConfiguration : IEntityTypeConfiguration<IdentityRole<Guid>>
{
public void Configure(EntityTypeBuilder<IdentityRole<Guid>> builder)
{
builder.HasData(
Enum.GetValues(typeof(UserRoles))
.Cast<UserRoles>()
.Select(roleEnum => new IdentityRole<Guid>
{
Id = Guid.NewGuid(),
Name = roleEnum.ToString(),
NormalizedName = roleEnum.ToString().ToUpper()
})
.ToArray());
}
}
}

View File

@@ -0,0 +1,109 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class SubmissionConfiguration : IEntityTypeConfiguration<Submission>
{
public void Configure(EntityTypeBuilder<Submission> builder)
{
// 1. 设置表名
builder.ToTable("submissions");
// 2. 设置主键
builder.HasKey(s => s.Id);
// 3. 配置列名、必需性、精度及其他属性
// Id
builder.Property(s => s.Id)
.HasColumnName("id");
// AssignmentId
builder.Property(s => s.AssignmentId)
.HasColumnName("assignment_id")
.IsRequired();
// StudentId
builder.Property(s => s.StudentId)
.HasColumnName("student_id")
.IsRequired();
// AttemptNumber
// 注意:如果 AttemptNumber 应该是一个递增的数字Guid 可能不是最合适的类型。
// 但根据你的定义,这里按 Guid 类型配置。
builder.Property(s => s.AttemptNumber)
.HasColumnName("attempt_number")
.IsRequired();
// SubmissionTime
builder.Property(s => s.SubmissionTime)
.HasColumnName("submission_time"); // 没有 [Required] 属性,所以可以是可空的
// OverallGrade
builder.Property(s => s.OverallGrade)
.HasColumnName("overall_grade")
.HasPrecision(5, 2); // 应用精度设置
// OverallFeedback
builder.Property(s => s.OverallFeedback)
.HasColumnName("overall_feedback");
// GradedBy (现为 Guid? 类型)
builder.Property(s => s.GradedBy)
.HasColumnName("graded_by"); // 作为可空外键,不需要 IsRequired()
// GradedAt
builder.Property(s => s.GradedAt)
.HasColumnName("graded_at");
// IsDeleted
builder.Property(s => s.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false);
// Status (枚举作为字符串存储)
builder.Property(s => s.Status)
.HasColumnName("status")
.IsRequired()
.HasConversion<string>() // <--- 重要:将枚举存储为字符串
.HasMaxLength(15); // <--- 应用最大长度
// 4. 配置导航属性和外键关系
// ---
// 配置 Submission 到 Assignment 的关系 (多对一)
// 一个 Submission 属于一个 Assignment。
builder.HasOne(s => s.Assignment) // 当前 Submission 有一个 Assignment
.WithMany(a => a.Submissions) // 那个 Assignment 可以有多个 Submission
.HasForeignKey(s => s.AssignmentId) // 外键是 Submission.AssignmentId
.OnDelete(DeleteBehavior.Cascade); // 当 Assignment 被删除时,相关的 Submission 也级联删除。
// ---
// 配置 Submission 到 User (Student) 的关系 (多对一)
// 一个 Submission 由一个 User (Student) 提交。
builder.HasOne(s => s.Student) // 当前 Submission 有一个 Student (User)
.WithMany(u => u.SubmissionsAsStudent) // 那个 User (Student) 可以有多个 Submission
.HasForeignKey(s => s.StudentId) // 外键是 Submission.StudentId
.OnDelete(DeleteBehavior.Restrict); // 当 User (Student) 被删除时,如果还有其提交的 Submission则会阻止删除。
// ---
// 配置 Submission 到 User (Grader) 的关系 (多对一)
// 一个 Submission 可以由一个 User (Grader) 批改 (可选)。
builder.HasOne(s => s.Grader) // 当前 Submission 有一个 Grader (User),可以是空的
.WithMany(u => u.GradedSubmissions) // 那个 User (Grader) 可以批改多个 Submission
.HasForeignKey(s => s.GradedBy) // 外键是 Submission.GradedBy
.OnDelete(DeleteBehavior.SetNull); // 当 User (Grader) 被删除时,如果 GradedBy 是可空的,则将其设置为 NULL。
// 如果 GradedBy 是不可空的,需要改为 Restrict 或 Cascade。
// ---
// 配置 Submission 到 SubmissionDetail 的关系 (一对多)
// 一个 Submission 可以有多个 SubmissionDetail。
// 这个关系的外键配置通常在 "多" 的一方 (`SubmissionDetail` 实体) 进行。
builder.HasMany(s => s.SubmissionDetails) // 当前 Submission 有多个 SubmissionDetail
.WithOne(sd => sd.Submission); // 每一个 SubmissionDetail 都有一个 Submission
// .HasForeignKey(sd => sd.SubmissionId); // 外键的配置应在 `SubmissionDetailConfiguration` 中进行
}
}
}

View File

@@ -0,0 +1,105 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class SubmissionDetailConfiguration : IEntityTypeConfiguration<SubmissionDetail>
{
public void Configure(EntityTypeBuilder<SubmissionDetail> builder)
{
// 1. 设置表名
// 将此实体映射到数据库中名为 "submission_details" 的表。
builder.ToTable("submission_details");
// 2. 设置主键
// 将 Id 属性设置为主键。
builder.HasKey(sd => sd.Id);
// 3. 配置列名、必需性、精度及其他属性
// Id 属性对应的数据库列名为 "id"。
builder.Property(sd => sd.Id)
.HasColumnName("id");
// SubmissionId 属性对应的数据库列名为 "submission_id",并设置为必需字段。
builder.Property(sd => sd.SubmissionId)
.HasColumnName("submission_id")
.IsRequired();
// StudentId 属性对应的数据库列名为 "student_id",并设置为必需字段。
// 此外键指向 User 实体,代表提交该详情的学生。
builder.Property(sd => sd.StudentId)
.HasColumnName("student_id")
.IsRequired();
// AssignmentQuestionId 属性对应的数据库列名为 "assignment_question_id",并设置为必需字段。
builder.Property(sd => sd.AssignmentQuestionId)
.HasColumnName("assignment_question_id")
.IsRequired();
// StudentAnswer 属性对应的数据库列名为 "student_answer"。
builder.Property(sd => sd.StudentAnswer)
.HasColumnName("student_answer"); // string 默认可空
// IsCorrect 属性对应的数据库列名为 "is_correct"。
builder.Property(sd => sd.IsCorrect)
.HasColumnName("is_correct"); // bool? 默认可空
// PointsAwarded 属性对应的数据库列名为 "points_awarded",并设置精度。
builder.Property(sd => sd.PointsAwarded)
.HasColumnName("points_awarded")
.HasPrecision(5, 2); // 应用 [Precision(5, 2)] 设置
// TeacherFeedback 属性对应的数据库列名为 "teacher_feedback"。
builder.Property(sd => sd.TeacherFeedback)
.HasColumnName("teacher_feedback"); // string 默认可空
// CreatedAt 属性对应的数据库列名为 "created_at",设置为必需字段,并在添加时自动生成值。
builder.Property(sd => sd.CreatedAt)
.HasColumnName("created_at")
.IsRequired()
.ValueGeneratedOnAdd(); // 在实体首次保存时自动设置值
// UpdatedAt 属性对应的数据库列名为 "updated_at",设置为必需字段,并在添加或更新时自动生成值,同时作为并发令牌。
builder.Property(sd => sd.UpdatedAt)
.HasColumnName("updated_at")
.IsRequired()
.ValueGeneratedOnAddOrUpdate() // 在实体添加或更新时自动设置值
.IsConcurrencyToken(); // 用作乐观并发控制,防止同时修改同一记录
// IsDeleted 属性对应的数据库列名为 "deleted",并设置默认值为 false。
builder.Property(sd => sd.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 常用作软删除标记
// 4. 配置导航属性和外键关系
// ---
// 配置 SubmissionDetail 到 Submission 的关系 (多对一)
// 一个 SubmissionDetail 记录属于一个 Submission。
builder.HasOne(sd => sd.Submission) // 当前 SubmissionDetail 有一个 Submission
.WithMany(s => s.SubmissionDetails) // 那个 Submission 可以有多个 SubmissionDetail 记录
.HasForeignKey(sd => sd.SubmissionId) // 外键是 SubmissionDetail.SubmissionId
.OnDelete(DeleteBehavior.Cascade); // 当 Submission 被删除时,相关的 SubmissionDetail 记录也级联删除。
// ---
// 配置 SubmissionDetail 到 User (作为 Student) 的关系 (多对一)
// 一个 SubmissionDetail 记录与一个 User (提交该详情的学生) 相关联。
// 假设 `User` 实体中有一个名为 `SubmissionDetailsAsStudent` 的 `ICollection<SubmissionDetail>` 集合属性。
builder.HasOne(sd => sd.User) // 当前 SubmissionDetail 有一个 User (作为学生)
.WithMany(u => u.SubmissionDetails)
.HasForeignKey(sd => sd.StudentId) // 外键是 SubmissionDetail.StudentId
.OnDelete(DeleteBehavior.Restrict); // 当 User (学生) 被删除时,如果他/她还有提交详情,则会阻止删除。
// 这是一个更安全的选择,以防止意外数据丢失。
// ---
// 配置 SubmissionDetail 到 AssignmentQuestion 的关系 (多对一)
// 一个 SubmissionDetail 记录对应一个 AssignmentQuestion。
builder.HasOne(sd => sd.AssignmentQuestion) // 当前 SubmissionDetail 有一个 AssignmentQuestion
.WithMany(aq => aq.SubmissionDetails) // 那个 AssignmentQuestion 可以有多个 SubmissionDetail 记录
.HasForeignKey(sd => sd.AssignmentQuestionId) // 外键是 SubmissionDetail.AssignmentQuestionId
.OnDelete(DeleteBehavior.Cascade); // 当 AssignmentQuestion 被删除时,相关的 SubmissionDetail 记录也级联删除。
}
}
}

View File

@@ -0,0 +1,76 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
// 映射到表名:如果 User 类上没有 [Table("users")],默认是 "AspNetUsers"。
// 显式指定可以确保你的数据库表名和你期望的一致。
builder.ToTable("AspNetUsers");
// IdentityUser 的 Id 属性和其他标准属性(如 UserName, Email 等)
// 大多由 IdentityDbContext 自动处理,通常不需要在这里显式配置主键或默认列。
// 配置自定义属性
builder.Property(u => u.RefreshToken)
.HasColumnName("refresh_token");
builder.Property(u => u.RefreshTokenExpiryTime)
.HasColumnName("refresh_token_expiry_time");
builder.Property(u => u.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 软删除标记,默认 false
// 配置导航属性 (User 作为关系的“一”或“主”方)
// User 作为老师,与 ClassTeacher 的关系 (一对多)
builder.HasMany(u => u.TaughtClassesLink) // 一个 User (老师) 可以教授多个班级
.WithOne(ct => ct.Teacher) // 一个 ClassTeacher 记录对应一个 Teacher
.HasForeignKey(ct => ct.TeacherId) // 外键在 ClassTeacher.TeacherId
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果老师有任教记录,则不允许删除
// User 作为学生,与 ClassStudent 的关系 (一对多)
builder.HasMany(u => u.EnrolledClassesLink) // 一个 User (学生) 可以注册多个班级
.WithOne(cs => cs.Student) // 一个 ClassStudent 记录对应一个 Student
.HasForeignKey(cs => cs.StudentId) // 外键在 ClassStudent.StudentId
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果学生有注册记录,则不允许删除
// User 作为创建者,与 Question 的关系 (一对多)
builder.HasMany(u => u.CreatedQuestions) // 一个 User 可以创建多个题目
.WithOne(q => q.Creator) // 一个 Question 对应一个 Creator
.HasForeignKey(q => q.CreatedBy) // 外键在 Question.CreatedBy
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果创建者有题目,则不允许删除
// User 作为创建者,与 Assignment 的关系 (一对多)
builder.HasMany(u => u.CreatedAssignments) // 一个 User 可以创建多个作业
.WithOne(a => a.Creator) // 一个 Assignment 对应一个 Creator
.HasForeignKey(a => a.CreatedBy) // 外键在 Assignment.CreatedBy
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果创建者有作业,则不允许删除
// User 作为学生,与 SubmissionDetail 的关系 (一对多)
// 尽管 SubmissionDetail 也可以通过 Submission 间接关联到 User
// 但这里提供了直接访问的导航属性,以方便直接查询学生的所有作答详情。
builder.HasMany(u => u.SubmissionDetails) // 一个 User (学生) 可以有多个提交详情记录
.WithOne(sd => sd.User) // 一个 SubmissionDetail 对应一个 User (学生)
.HasForeignKey(sd => sd.StudentId) // 外键在 SubmissionDetail.StudentId
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果学生有提交详情,则不允许删除
// User 作为学生,与 Submission 的关系 (一对多)
builder.HasMany(u => u.SubmissionsAsStudent) // 一个 User (学生) 可以有多个提交记录
.WithOne(s => s.Student) // 一个 Submission 对应一个 Student
.HasForeignKey(s => s.StudentId) // 外键在 Submission.StudentId
.OnDelete(DeleteBehavior.Restrict); // 限制删除:如果学生有提交记录,则不允许删除
// User 作为批改者,与 Submission 的关系 (一对多)
builder.HasMany(u => u.GradedSubmissions) // 一个 User (批改者) 可以批改多个提交
.WithOne(s => s.Grader) // 一个 Submission 对应一个 Grader
.HasForeignKey(s => s.GradedBy) // 外键在 Submission.GradedBy
.OnDelete(DeleteBehavior.SetNull); // 因为 GradedBy 是可空的,所以批改者删除时,设为 NULL
}
}
}