Ahmed Fathy
Ahmed Fathy

Reputation: 539

Entity Framework Code-First treats my code as one to many , although I want it to be many to many relationship

I'm trying to make a database where one of the tables has a one to one relationship and a many to many relationship ... The database created seems to be working as one to one relationship only. here's my model:

public class Trip
    {
        public int ID { get; set; }
        public virtual ICollection<Place> PassingBy { get; set; }
        public Place Origin { get; set; }
        public Place Destination { get; set; }
        public DateTime Time { get; set; }
        public int EmptySlots { get; set; }
        public virtual ICollection<Person> Attendants { get; set; }
        public string AccessKey { get; set; }

    }

    public class Person
    {
        public int ID { get; set; }
        public string Username { get; set; }
        public virtual ICollection<Trip> Trips { get; set; }
    }

    public class Place
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Trip> Trips { get; set; }
    }


    public class GARDB : DbContext
    {
        public DbSet<Trip> Trips { get; set; }
        public DbSet<Person> Persons { get; set; }
        public DbSet<Place> Places { get; set; }
    }

Now, when I do add-migrations and update database. The database has a table called "PersonTrip" which is needed for the many to many relationship. But there is no table for "PlaceTrip" . After some trial and error. I found out it's because of these lines:

    public Place Origin { get; set; }
    public Place Destination { get; set; }

They cause the relation to be one to many. Non many to many as I want to to be. Is there a way to make the Place-Trip relation like the Person-Trip one but without removing those 2 lines.

Thanks!

EDIT: The Solution

I didn't make clear what I wanted. I wanted to make the line

public virtual ICollection<Place> PassingBy { get; set; }

to be mapped to the line

public virtual ICollection<Trip> Trips { get; set; }

in the Place class. The solution I got to from the answers was this:

replace :

 public virtual ICollection<Place> PassingBy { get; set; }

with:

[InverseProperty("Trips")]
public virtual ICollection<Place> PassingBy { get; set; }

and replace :

public virtual ICollection<Trip> Trips { get; set; }

in the Place class with:

[InverseProperty("PassingBy")]
public virtual ICollection<Trip> Trips { get; set; }

and add this include at the top:

using System.ComponentModel.DataAnnotations.Schema;

Upvotes: 2

Views: 125

Answers (2)

Cerebrate
Cerebrate

Reputation: 1391

What's confusing it, in effect, is that you have two ends for the relationship on Trip:

public Place Origin { get; set; }
public Place Destination { get; set; }

(which should also be virtual, BTW), and only one on Place:

public virtual ICollection<Trip> Trips { get; set; }

...so Entity Framework can't figure out which goes with what.

You need to add both a second navigation property to Place, and some data annotation attributes to tell Entity Framework how they relate to form the two halves of the many-to-many relationship. Omitting the other properties and the context for clarity, like this:

public class Trip
{
    [InverseProperty ("AsOrigin")]
    public virtual Place Origin { get; set; }

    [InverseProperty ("AsDestination")]
    public virtual Place Destination { get; set; }
}

public class Place
{
    [InverseProperty ("Origin")]
    public virtual ICollection<Trip> AsOrigin { get; set; }

    [InverseProperty ("Destination")]
    public virtual ICollection<Trip> AsDestination { get; set; }
}

If you need a single property on Place that gives you all trips that it's involved in, either as origin or destination, you can recreate it using the navigation properties like this:

[NotMapped]
public ICollection<Trip> Trips
{
    get
    {
        return AsOrigin.Concat(AsDestination).Distinct();
    }
}

Upvotes: 1

Corey Adler
Corey Adler

Reputation: 16149

I'm not a big fan of the relationships that you're creating here, but that's something for your DBA to chew on. In the meantime, if you want to do something like this, may I suggest utilizing the answer to this SO question which is trying to do something very similar. You would need to add a couple of more properties and set up foreign key relations between the tables, but it should work out just fine.

Upvotes: 0

Related Questions