Search code examples
entity-framework-coreef-code-firstasp.net-core-2.0

EF-core howto set up many2many relationship to 'itself'


Case : Teammember is anyone who is working at a hospital. Teammembers (doctors) can have other teammembers as their assistants (nurse or another doctor), or teammembers can be an assistant of another teammember.

Classes

public class Teammember 
{
    Public int Id {get; set;}
    public ICollection<AssistantLink> AssistantLinks { get; } = new List<AssistantLink>();
    public ICollection<Teammember> Assistants => AssistantLinks.Where(x => x.CareProviderId == Id).Select(x => x.Assistant).ToList();
    public ICollection<Teammember> CareProviders => AssistantLinks.Where(x => x.AssistantId == Id).Select(x => x.CareProvider).ToList();
}

To set up this relationship I created a class 'AssistantLink'

   public class AssistantLink : ModelBase
    {
        public int CareProviderId { get; set; }
        public Teammember CareProvider { get; set; }
        public int AssistantId { get; set; }
        public Teammember Assistant { get; set; }
    }

In the DB context I tried to wire up this relationship via:

    builder.Entity<AssistantLink>().HasKey(x => new { x.CareProviderId, x.AssistantId });
    builder.Entity<AssistantLink>().HasOne(x => x.CareProvider).WithMany("AssistantLinks"); //use string notation because navigation property is private
    builder.Entity<AssistantLink>().HasOne(x => x.Assistant).WithMany("AssistantLinks");

When creating a db-migration I get the following

ERROR : Cannot create a relationship between 'Teammember.AssistantLinks' and 'AssistantLink.Assistant', because there already is a relationship between 'Teammember.AssistantLinks' and 'AssistantLink.CareProvider'. Navigation properties can only participate in a single relationship.

Any suggestions how to set up this the correct way ?


Solution

  • It's not possible to have just one collection with relations. You need two - one with assistantlinks the teammember equals CareProvider and second with assistantlinks the teammember equals Assistant.

    Model

    public class Teammember
    {
        public int Id { get; set; }
        public ICollection<AssistantLink> Assistants { get; set; } 
        public ICollection<AssistantLink> Providers { get; set; }
    }
    public class AssistantLink : ModelBase
    {
        public int CareProviderId { get; set; }
        public Teammember CareProvider { get; set; }
        public int AssistantId { get; set; }
        public Teammember Assistant { get; set; }
    }
    

    Configuration:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AssistantLink>()
                .HasKey(x => new { x.CareProviderId, x.AssistantId });
            modelBuilder.Entity<AssistantLink>()
                .HasOne(al => al.CareProvider)
                .WithMany(c => c.Providers)
                .HasForeignKey(al => al.CareProviderId)
                .OnDelete(DeleteBehavior.Restrict);
            modelBuilder.Entity<AssistantLink>()
                .HasOne(al => al.Assistant)
                .WithMany(a => a.Assistants)
                .HasForeignKey(al => al.AssistantId)
                .OnDelete(DeleteBehavior.Restrict); 
        }