Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Code First: how to map multiple self-referencing many-to-many relationships

I have created an entity type that has multiple collection properties that reference items of the same type. In other words, it reflects a single database table in which the rows are arbitrarily grouped, such that a row may appear in multiple groups.

In the following simplified example, the Person class has Brothers and Sisters collection properties that also reference Person entities:

public class Person
{
    public Person()
    {
        Brothers = new Collection<Person>();
        Sisters = new Collection<Person>();
    }

    [Key]
    public string Name { get; set; }

    public int Age { get; set; }

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

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

Entity Framework seems to think that this is a valid model, but interprets it to create a single PersonPersons join table, which fails to reflect the separation of brother and sister relationships.

I assume the solution is to use the fluent API to explicitly map separate join tables for the two relationships but, despite extensive experimentation, I have been unable to get this to work.

Any suggestions please?

Thanks, Tim

like image 773
Tim Coulter Avatar asked Nov 14 '14 13:11

Tim Coulter


People also ask

How do you establish a self referencing many-to-many relationship?

A self-referencing many-to-many relationship exists when a given record in the table can be related to one or more other records within the table and one or more records can themselves be related to the given record.

How does Entity Framework handle many-to-many relationships in core?

Many-to-many relationships require a collection navigation property on both sides. They will be discovered by convention like other types of relationships. The way this relationship is implemented in the database is by a join table that contains foreign keys to both Post and Tag .

What is many-to-many relationships in code first approach?

A many-to-many relationship is defined in code by the inclusion of collection properties in each of the entities - The Categories property in the Book class, and the Books property in the Category class: public class Book. { public int BookId { get; set; }


1 Answers

By adding this in the DbContext.OnModelCreating method:

UPDATE Added table-naming map according to nameEqualsPNamePrubeGoldberg's comment above:

modelBuilder.Entity<Person>().HasMany(x => x.Brothers).WithMany()
    .Map(x => x.ToTable("Person_Brothers"));
modelBuilder.Entity<Person>().HasMany(x => x.Sisters).WithMany()
    .Map(x => x.ToTable("Person_Sisters"));

I got this unit test to pass

[TestMethod]
public void TestPersons()
{
    var brother = new Person() { Name = "Brother 1", Age = 10 };
    var sister = new Person() { Name = "Sister 1", Age = 12 };
    var sibling = new Person() { Name = "Sibling 1", Age = 18 };
    sibling.Brothers.Add(brother);
    sibling.Sisters.Add(sister);

    using (var db = new MyDatabase())
    {
        db.Persons.Add(brother);
        db.Persons.Add(sister);
        db.Persons.Add(sibling);

        db.SaveChanges();
    }

    using (var db = new MyDatabase())
    {
        var person = db.Persons
            .Include(x => x.Sisters)
            .Include(x => x.Brothers)
            .FirstOrDefault(x => x.Name.Equals(sibling.Name));

        Assert.IsNotNull(person, "No person");
        Assert.IsTrue(person.Brothers.Count == 1, "No brothers!");
        Assert.IsTrue(person.Sisters.Count == 1, "No sisters");
    }
}

That also creates the link tables you're talking about.

like image 79
Daniel Persson Avatar answered Nov 04 '22 10:11

Daniel Persson