I'm trying to follow DDD using EF Core and in my model I have the following:
private List<TeamPerson> _personLinks;
public IReadOnlyCollection<TeamPerson> PersonLinks => _personLinks?.ToList().AsReadOnly();
public IReadOnlyCollection<Person> Members => _personLinks?.Select(l => l.Person).ToList().AsReadOnly();
I want to encapsulate the relationship between Team and Person models. I must say right away that there is a mapping for the Person model and it works.
But if I specify property access mode:
builder.HasMany(e => e.PersonLinks)
.WithOne(e => e.Team)
.HasForeignKey(e => e.TeamId)
.Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.HasMany(e => e.TeamLinks)
.WithOne(e => e.Person)
.HasForeignKey(e => e.PersonId)
.Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
And if I try to get dbContext.Teams.Include(t => t.PersonLinks).ThenInclude(t => t.Person), I'm getting an error: "...PersonId1 column doesn't exist...", I have this for TeamPerson model mapping:
builder.Property(e => e.PersonId).IsRequired().HasColumnName("person_id");
What is my mistake or is there any other way to reach the encapsulation for the collection here?
I think you need to configure a one-to-many relationship between both Person and PersonLinks as well between Team and PersonLinks to achieve the many-to-many relationship.
builder.Entity<PersonLink>()
.HasKey(personLink => new { personLink.TeamId, personLink.PersonId });
builder.Entity<PersonLink>()
.HasOne<Team>(personLink => personLink.Team)
.WithMany(team => team.PersonLinks)
.HasForeignKey(personLink => personLink.TeamId);
builder.Entity<PersonLink>()
.HasOne<Person>(personLink => personLink.Person)
.WithMany(person => person.PersonLinks)
.HasForeignKey(personLink => personLink.PersonId);
This of course will only work if your PersonLink class looks something like this:
public class PersonLink
{
public int PersonId { get; set; }
public Person Person { get; set; }
public int TeamId { get; set; }
public Team Team { get; set; }
}
For the id data types I assumed a simple int which could be string or GUID (or even some custom type in your code) but I think this is not that important here.
Note: I could not resist to rename PersonLinks to PersonLink it feels more natural to me because the class represents one person link between Team and Person from my point-of-view. Also, I know it somehow a convention to use abbreviated parameter names for lambda expressions but I think, especially in this case, it is easier to read instead of using p (for Person) and pl (for PersonLink) - maybe it's just a matter of taste in this case...