Reputation: 3102
I have a Program entity like this
public class Program : IEntityBase
{
public int Id { get; set; }
public string ProgramCode { get; set; }
public string Name { get; set; }
public int DegreeTypeID { get; set; }
public DegreeType DegreeType { get; set; }
}
with programCode created as a unique key with this implementation
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Program>().HasAlternateKey(d => d.ProgramCode).HasName("AK_ProgramCode");
}
I have another entity ApplicantProgram with this definition
public class ApplicantProgram : IEntityBase
{
public int Id { get; set; }
public int ApplicantID { get; set; }
public Applicant Applicant { get; set; }
[Required]
public string FirstChoiceID { get; set; }
[Required]
public string SecondChoiceID { get; set; }
[Required]
public string ThirdChoiceID { get; set; }
public string SessionID { get; set; }
}
Which has FirstChoiceID, SecondChoiceID & ThirdChoiceID having ProgramCode in the program table. Now these are my questions,
Thank you for pausing to read this.
Upvotes: 3
Views: 3360
Reputation: 205629
(1) How do I get Program.Name property from ApplicantProgram knowing the FirstChoiceID that is to link to Program.ProgramCode?
There is nothing specific to EF here, you could use the typical data correlation operator - join
. Just because you have 3 related properties, you would need 3 join
s as well:
var query =
from applicantProgram in db.ApplicantPrograms
join firstChoice in db.Programs on applicantProgram.FirstChoiceID equals firstChoice.ProgramCode
join secondChoice in db.Programs on applicantProgram.SecondChoiceID equals secondChoice.ProgramCode
join thirdChoice in db.Programs on applicantProgram.ThirdChoiceID equals thirdChoice.ProgramCode
select new
{
ApplicantProgram = applicantProgram,
FirstChoice = firstChoice,
SecondChoice = secondChoice,
ThirdChoice = thirdChoice,
};
Inside the select
, you could get the whole related objects as above, or specific properties like firstChoice.Name
, secondChoice.Name
etc.
But you won't need all that in EF once you define the navigation properties, which leads us to:
(2) Is it possible to create a Navigation property to program from ApplicantProgram?
(3) How do I create a foreign key from ApplicantProgram to Program based off the ChoiceIDs that should link to Program.ProgramCode without using Program.Id?
These two are interrelated. While it's possible to define a FK without navigation property, the navigation property would allow you simple access to related entity properties inside the LINQ queries as well as simple eager loading the related entity as part of the entity which is using it.
Start by adding the 3 navigation properties (one for each FK property) in ApplicantProgram
class:
public Program FirstChoice { get; set; }
public Program SecondChoice { get; set; }
public Program ThirdChoice { get; set; }
and the following fluent configuration:
builder.Entity<ApplicantProgram>()
.HasOne(e => e.FirstChoice)
.WithMany()
.HasForeignKey(e => e.FirstChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicantProgram>()
.HasOne(e => e.SecondChoice)
.WithMany()
.HasForeignKey(e => e.SecondChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicantProgram>()
.HasOne(e => e.ThirdChoice)
.WithMany()
.HasForeignKey(e => e.ThirdChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
What we have here is the standard many-to-one
relationship configuration - with HasOne(...)
specifying the reference navigation property, WithMany()
specifying no corresponding collection navigation property, HasForeighKey(...)
specifying the corresponding FK property, and also the typical for multiple relationships to one and the same table turning off the cascade delete in order to avoid the multiple cascade paths problem.
What is specific thought (and is the EF Core improvement over EF6) is the HasPrincipalKey(...)
method which allows you to specify other unique key property instead of the PK (by default) to be used by the FK relationship. Which in the combination with HasAlternateKey(...)
on the other end allows to achieve the desired FK relationship setup.
And basically that's all. Now the query from (1) could be simply
var query =
from applicantProgram in db.ApplicantPrograms
select new
{
applicantProgram,
firstChoice = applicantProgram.FirstChoice,
secondChoice = applicantProgram.SecondChoice,
thirdChoice = applicantProgram.ThirdChoice,
};
Similar to (1), you could project the whole related objects or just properties you need.
Or you could get the ApplicantProgram
instances with populated related Program
properties by adding Include
operators to the ApplicantProgram
query (the so called eager loading):
var query = db.ApplicantPrograms
.Include(applicantProgram => applicantProgram.FirstChoice)
.Include(applicantProgram => applicantProgram.SecondChoice)
.Include(applicantProgram => applicantProgram.ThirdChoice);
Upvotes: 2