Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly model self-referencing multi-parent relationship using entity framework

I am trying to model the following self-referencing person class in EF6.

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }

    public int? MotherId { get; set; }
    public Person Mother { get; set; }

    public int? FatherId { get; set; }
    public Person Father { get; set; }

    public virtual ICollection<Person> Children { get; set; }
}

And my DbContext looks like this:

public virtual DbSet<Person> People { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOptional(m => m.Mother)
        .WithMany(c => c.Children)
        .HasForeignKey(m => m.MotherId);

     modelBuilder.Entity<Person>()
        .HasOptional(f => f.Father)
        .WithMany(c => c.Children)
        .HasForeignKey(f => f.FatherId);
}

When trying to add a person to the database using the following code:

db.People.Add(new Person { Name = "Jane Doe" });

I get this error:

An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll

Additional information: Sequence contains no matching element

What does this error mean, and how do I correct it? Optionally, is there a better way to model this object (ex: using subclass of Mother:Person and Father:Person)?

like image 297
Jens Ehrich Avatar asked May 01 '26 15:05

Jens Ehrich


1 Answers

I have come up with the following solution that generates a clean database and allows for greater flexibility when adding relationships:

public interface IParent
{
    ICollection<Person> Children { get; set; }
}

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }

    public int? MotherId { get; set; }
    public Female Mother { get; set; }

    public int? FatherId { get; set; }
    public Male Father { get; set; }
}

public class Male : Person, IParent
{
    public virtual ICollection<Person> Children { get; set; }
}

public class Female : Person, IParent
{
    public virtual ICollection<Person> Children { get; set; }
}

The DbContext only contains:

public virtual DbSet<Person> People { get; set; }
public virtual DbSet<Female> Females { get; set; }
public virtual DbSet<Male> Males { get; set; }

And the resulting database looks like this:

ID  Name    MotherId    FatherId    Discriminator
1   Jane    NULL        NULL        Female
2   John    NULL        NULL        Male
3   Jimmy   1           2           Male
4   Jenn    1           2           Female

This solution also gives the flexibility of adding relationships in multiple ways:

mom.Children.Add(son); // or
son.Mother = mom;
like image 184
Jens Ehrich Avatar answered May 04 '26 05:05

Jens Ehrich