【问题标题】:EF Core Unique Constraint across multiple tables跨多个表的 EF Core 唯一约束
【发布时间】:2018-09-14 14:04:51
【问题描述】:

我如何创建/设计关系和唯一约束:

一个项目有许多子作业。一个 SUBJOB 有很多任务。 TASK 有一个编号,该编号在单个 PROJECT 中必须是唯一的。

我尝试了以下关系,但我不知道如何为 Task.NumberProject.ProjectId 强制执行唯一约束:

public class ProjectContext : DbContext
{
    public DbSet<Project> Project { get; private set; }

    public DbSet<SubJob> SubJob { get; private set; }

    public DbSet<Task> Task { get; private set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Project>(b =>
        {
            b.HasKey(p => p.ProjectId);
            b.HasAlternateKey(p => p.Code);
        });

        modelBuilder.Entity<SubJob>(b =>
        {
            b.HasKey(s => s.SubJobId);
            b.HasOne(s => s.Project).WithMany(p => p.SubJobs).HasForeignKey(s => s.ProjectId);
            b.HasAlternateKey(s => new { s.ProjectId, s.Code });
        });

        modelBuilder.Entity<Task>(b =>
        {
            b.HasKey(l => l.TaskId);
            b.HasOne(l => l.SubJob).WithMany(s => s.Tasks).HasForeignKey(l => l.SubJobId);
        });
    }
}

public class Project
{
    public int ProjectId { get; private set; }

    public string Code { get; set; }

    public ICollection<SubJob> SubJobs { get; private set; } = new List<SubJob>();
}

public class SubJob
{
    public int SubJobId { get; private set; }

    public int ProjectId { get; private set; }

    public Project Project { get; set; }

    public string Code { get; set; }

    public ICollection<Task> Tasks { get; private set; } = new List<Task>();
}

public class Task
{
    public int TaskId { get; private set; }

    public int SubJobId { get; private set; }

    public SubJob SubJob { get; set; }

    public int Number { get; set; }
}

【问题讨论】:

    标签: sql database database-design entity-framework-core


    【解决方案1】:

    我看到了两种可能性。对子类使用自然键,因此 Subjob 可能是 {ProjectId, JobCode} 而 Task 可能是 {ProjectId, JobCode, Number}。但如果 {ProjectId, Number} 必须是唯一的,那么这将是 PK,而 JobCode 将是非空 FK,但不是 PK 的一部分。

    或者,如果您的数据库支持索引视图,您可以在 {ProjectId, Number} 上创建一个具有唯一索引的视图。

    【讨论】:

    • 我不赞成自然键,但 +1 索引视图解决方法:)
    【解决方案2】:

    要在Tasks 表上定义唯一约束,您需要在Task 类中获取ProjectId 的值。修改后的Task 实体将如下所示。

    public class Task
    {
        public int TaskId { get; private set; }
        public int SubJobId { get; private set; }
        public SubJob SubJob { get; set; }
        public int Number { get; set; }
        public int ProjectId { get; set; } // Added property
    }
    

    因为每个Task 都有一个SubJob 并且每个SubJob 都有一个Project,所以Task.ProjectId 应该等于Task.SubJob.ProjectId(反过来从SubJob.Project.ProjectId 中获取值)。为了确保传递此值,我们可以定义TaskSubJob 之间的关系,以使用如下复合键

    modelBuilder.Entity<Task>(b =>
    {
        b.HasKey(l => l.TaskId);
        b.HasOne(l => l.SubJob).WithMany(s => s.Tasks).HasForeignKey(l => new { l.SubJobId, l.ProjectId })
            .HasPrincipalKey(l => new { l.SubJobId, l.ProjectId });
        b.HasAlternateKey(t => new { t.Number, t.ProjectId });
    });
    

    然后我们可以使用HasAlternateKey 定义NumberProjectId 上的唯一约束。

    您也可以使用带有ProjectId 的复合键作为一部分,但这可能需要为 PK 的其他部分设置值生成。

    除非您确实需要唯一约束,否则请考虑使用唯一索引。更多信息可以找到here

    【讨论】:

      猜你喜欢
      • 2012-05-07
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多