我有两个多对多关系的表。我使用时态表来跟踪记录的更改,以便我可以查看更新和删除以进行审核或比较更改。在这种情况下,我有很多老师和很多课程。每位教师可以教授多门课程,每门课程也可以由多位教师教授。对于每个
Teacher_Courses
,还有有关该特定班级的数据,例如 NumberOfStudents 或 ClassPreferences。在数据库中,仅仅因为课程被删除,我不想删除有关该课程的详细信息Teacher_Courses
,因为系统中的其他对象依赖于该数据。因此,我将这些详细信息添加到 Teacher_CoursesDetails
表中。我想设计一组表,也使用时态表,这样:
请勿使用“软删除”执行此操作。这将打破数据库设计其余部分的模式。
保留时态表,以便在各自的时态表中跟踪所有更新/删除
public class TeacherClass
{
public int Id { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public ICollection<Teacher_Courses> Teacher_Courses { get; set; }
}
public class Teacher_Courses
{
public int Id { get; set; } // Add a primary key
public int TeacherId { get; set; }
public int CourseId { get; set; }
public TeacherClass TeacherClass { get; set; }
public CourseClass CourseClass { get; set; }
public Teacher_CoursesDetails Details { get; set; }
}
public class Teacher_CoursesDetails
{
public int Id { get; set; } // Add a primary key
public string ClassPreferences { get; set; } = string.Empty;
public int NumberOfStudents { get; set; }
public int Teacher_CoursesId { get; set; }
public Teacher_Courses Teacher_Courses { get; set; }
}
public class CourseClass
{
public int Id { get; set; }
public string CourseName { get; set; } = string.Empty;
public string CourseDescription { get; set; } = string.Empty;
public ICollection<Teacher_Courses> Teacher_Courses { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<TeacherClass> TeacherClass { get; set; }
public DbSet<Teacher_Courses> Teacher_Courses { get; set; }
public DbSet<CourseClass> CourseClass { get; set; }
public DbSet<Teacher_CoursesDetails> Teacher_CoursesDetails { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<TeacherClass>().ToTable("TeacherClass", c => c.IsTemporal());
builder.Entity<CourseClass>().ToTable("CourseClass", c => c.IsTemporal());
builder.Entity<Teacher_Courses>(entity =>
{
entity.ToTable("Teacher_Courses", c => c.IsTemporal());
entity.HasKey(e => e.Id);
entity.HasOne(e => e.TeacherClass)
.WithMany(s => s.Teacher_Courses)
.HasForeignKey(e => e.TeacherId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(e => e.CourseClass)
.WithMany(c => c.Teacher_Courses)
.HasForeignKey(e => e.CourseId)
.OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<Teacher_CoursesDetails>(entity =>
{
entity.ToTable("Teacher_CoursesDetails", c => c.IsTemporal());
entity.HasKey(e => e.Id);
entity.HasOne(d => d.Teacher_Courses)
.WithOne(t => t.Details)
.HasForeignKey<Teacher_CoursesDetails>(d => d.Teacher_CoursesId)
.OnDelete(DeleteBehavior.NoAction);
});
}
}
这里的问题是,当我尝试删除教师时,如果 Teacher_Courses 中有记录,我将收到 FK 约束错误。我可以将 Teacher_CoursesDetails OnDelete 设置为 SetNull,但是当用户删除教师时,它会将 Teacher_CoursesDetailsId 设置为 null,然后我将无法查询数据。有没有办法解决这个问题或者有更好的方法吗?
使用建议的软删除选项似乎与使用临时表的好处并不相符,我认为临时表应该跟踪所有更改,包括删除。 我最终做的是,在我想要防止删除的表中,将 DeleteBehavior 更改为 ClientSetNull。 这样,当父对象被删除时,子记录只是将 KF 设置为 null。 然后,我编写了中间件(使用 SaveChangesInterceptor),将另一个字段(我称为 ProxyFK)设置为已删除键的值。这样,我就可以根据它进行查询。 是否有理由认为这不是一种可接受的方法?
public class DeletedObjectInterceptor : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
var context = eventData.Context;
if (context is null) return result;
HandleDeletedConnectors(context);
return result;
}
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
var context = eventData.Context;
if (context is null) return ValueTask.FromResult(result);
HandleDeletedConnectors(context);
return ValueTask.FromResult(result);
}
private void HandleDeletedConnectors(DbContext context)
{
var deletedItems = context.ChangeTracker.Entries<CourseClass>()
.Where(e => e.State == EntityState.Deleted)
.Select(e => e.Entity)
.ToList();
foreach (var c in deletedItems)
{
var relatedItems = context.Set<Teacher_Courses>()
.Where(mc => mc.CourseId == c.Id)
.ToList();
foreach (var item in relatedItems)
{
c.ProxyId = item.ConnectorId;
context.Entry(c).State = EntityState.Modified;
}
}
}
}
在 Program.cs 中:
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
options.AddInterceptors(new DeletedObjectInterceptor());
});