Reputation: 6522
How do I create/design the relationship and unique constraint:
A PROJECT has many SUBJOBS. A SUBJOB has many TASKS. A TASK has a Number which must be unique across a single PROJECT.
I tried the following for the relationship but I can't figure out how to enforce the unique constraint for Task.Number
and Project.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; }
}
Upvotes: 0
Views: 1147
Reputation: 53
One option to achieve this is creating a foreign key in Task
class referencing Project
and then create a unique index that forces the uniqueness.
Task class would have this extra property:
public int ProjectId { get; private set; }
And your DbContext would look like this (when using FluentAPI):
modelBuilder.Entity<Task>()
.HasOne<Project>()
.WithMany()
.HasForeignKey(t => t.ProjectId);
modelBuilder.Entity<Task>()
.HasIndex(t => new { t.Number., t.ProjectId})
.IsUnique();
PS: Depending on your other relationships, you might need to change your deleting behavior to avoid cycles.
Upvotes: 0
Reputation: 2459
To define unique constraint on Tasks
table, you would need to get value of ProjectId
in Task
class. The modified Task
entity would look like this.
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
}
Since every Task
has a SubJob
and every SubJob
has a Project
, Task.ProjectId
should be equal to Task.SubJob.ProjectId
(which in turn takes value from SubJob.Project.ProjectId
). To make sure this value is passed around we can define a relationship between Task
& SubJob
to use composite keys like following
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 });
});
Then we can define the unique constraint over Number
& ProjectId
using HasAlternateKey
.
You could also use Composite Keys with ProjectId
as being part but that could require to setup value generation for other part of PK.
Unless you really need unique constraint, consider using unique index. More info can be found here
Upvotes: 0
Reputation: 383
I see two possibilities. Use natural keys for the child classes, so Subjob might be {ProjectId, JobCode} and Task might be {ProjectId, JobCode, Number}. But if the {ProjectId, Number} must be unique, then that would be the PK and JobCode would be a non-null FK, but not part of the PK.
Or, if your database supports indexed views you could create a view with a unique index on {ProjectId, Number}.
Upvotes: 1