When inserting data into a many-to-many relationship, should you insert to the join-table or to both original tables?
My table models:
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupMember> GroupMembers { get; set; }
The relationship between them is configured with Fluent API:
builder.Entity<GroupMembers>().HasKey(gm => new { gm.UserId, gm.GroupId });
builder.Entity<GroupMembers>().HasOne(gm => gm.Group).WithMany(group => group.GroupMembers).HasForeignKey(gm => gm.GroupId);
builder.Entity<GroupMembers>().HasOne(gm => gm.User).WithMany(user => user.GroupMembers).HasForeignKey(gm => gm.UserId);
public class Group
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public List<GroupMember> GroupMembers { get; set; } = new List<GroupMembers>();
}
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public List<GroupMembers> GroupMembers { get; set; } = new List<GroupMembers>();
}
public class GroupMembers
{
[Key]
public Guid UserId { get; set; }
public User User { get; set; }
[Key]
public Guid GroupId { get; set; }
public Group Group { get; set; }
}
Now, the question is; in which tables/classes should I insert the data about the group members? Is it like this:
GroupMembers groupMember = new GroupMembers
{
Group = group,
GroupId = group.Id,
User = user,
UserId = user.Id
};
user.GroupMembers.Add(groupMember);
group.GroupMembers.Add(groupMember)
_databaseContext.Users.Update(user);
_databaseContext.SaveChanges();;
_databaseContext.Groups.Update(group);
_databaseContext.SaveChanges();
Or like this, leaving the User and Group untouched, with the information about their relationship ONLY in the join-table:
GroupMembers groupMember = new GroupMembers
{
Group = group,
GroupId = group.Id,
User = user,
UserId = user.Id
};
_databaseContext.GroupMembers.Add(groupMember);
_databaseContext.SaveChanges();
As far as Entity Framework is concerned, this is not a many-to-many relationship
What you have here is three entity types with two one-to-many relationships defined between them. You might know that this is done to represent a many-to-many, but EF doesn't know that.
If I arbitrarily change the names of your entities while maintaining the structure, you wouldn't be able to tell if this was a many-to-many relationship or not.
Simple example:
public class Country {}
public class Company {}
public class Person
{
public int CountryOfBirthId { get; set; }
public virtual Country CountryOfBirth { get; set; }
public int EmployerId { get; set; }
public virtual Company Employer { get; set; }
}
You wouldn't initially think of Person
as the represenation of a many-to-many relationship between Country
and Company
, would you? And yet, this is structurally the same as your example.
Essentially, your handling of your code shouldn't be any different from how you handle any of your one-to-many relationships. GroupMembers
is a table (db set) like any else, and EF will expect you to treat it like a normal entity table.
The only thing that's different here is that because GroupMember
has two one-to-many relationships in which it is the "many", you therefore have to supply two FKs (one to each related entity). But the handling is exactly the same as if you had only one one-to-many relationship here.
In other words, add your groupMember
to the table itself:
GroupMembers groupMember = new GroupMembers
{
// You don't have to fill in the nav props if you don't need them
GroupId = group.Id,
UserId = user.Id
};
_databaseContext.GroupMembers.Add(groupMember);
_databaseContext.SaveChanges();
Note: The following only applies to non-Core Entity Framework, as EF Core does not yet support it.
An example of what would be a "real" many-to-many relationship in (non-Core) EF would be if the intermediary table was not managed by you, i.e.:
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
}
public class Group
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
In this scenario, EF will still generate the cross table in the database, but EF will hide this table from you. Here, you are expected to work via the nav props:
var user = myContext.Users.First();
var group = myContext.Groups.First();
user.Groups.Add(group);
myContext.SaveChanges();
Whether you use a "real" many-to-many relationship or manage the cross table yourself is up to you. I tend to only manage the cross table myself when I can't avoid it, e.g. when I want additional data on the cross table.