Fluent API, many-to-many in Entity Framework Core
EF Core 5.0 RC1+
As of EF Core 5.0 RC1, it's possible to do this without an explicit join table. EF Core is able to configure a mapping for the many-to-many relationship shown in your question without requiring you to create a PersonClub
type.
See What's New in EF Core 5.0, RC1, Many-to-many in the official docs for more information.
Previous Versions
This is not yet possible in EF Core without using an explicit class for the join. See here for an example of how to do that.
There's an open issue on Github asking for the ability to do this without the need for an explicit class, but it has not yet been completed.
Using your scenario, the example I linked would recommend the following entity classes:
public class Person
{
public int PersonId { get; set; }
public virtual ICollection<PersonClub> PersonClubs { get; set; }
}
public class Club
{
public int ClubId { get; set; }
public virtual ICollection<PersonClub> PersonClubs { get; set; }
}
public class PersonClub
{
public int PersonId { get; set; }
public Person Person { get; set; }
public int ClubId { get; set; }
public Club Club { get; set; }
}
The following OnModelCreating
would then be used for setup:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonClub>()
.HasKey(pc => new { pc.PersonId, pc.ClubId });
modelBuilder.Entity<PersonClub>()
.HasOne(pc => pc.Person)
.WithMany(p => p.PersonClubs)
.HasForeignKey(pc => pc.PersonId);
modelBuilder.Entity<PersonClub>()
.HasOne(pc => pc.Club)
.WithMany(c => c.PersonClubs)
.HasForeignKey(pc => pc.ClubId);
}
Be sure to go to the open issue I linked and voice your frustration if you feel the need.
EDIT: The open issue suggests using a simple Select
to navigate through this somewhat cumbersome hierarchy. In order to get from a PersonId
to a collection of Club
s, you can use SelectMany
. e.g.:
var clubs = dbContext.People
.Where(p => p.PersonId == id)
.SelectMany(p => p.PersonClubs);
.Select(pc => pc.Club);
I can't vouch for whether this is truly a "best practice", but it should certainly do the trick and I think its fair to say it's not overly ugly.
ManyToMany Relation in EF Core fluent API
You need to use some of the other UsingEntity
overloads which allow you to configure the left and right navigations.
For instance
entityTypeBuilder1
.HasMany(lambdaManyFirst)
.WithMany(lambdaManySecond)
.UsingEntity<Dictionary<string, object>>(joinEntityName,
j => j.HasOne<TEntity2>().WithMany().HasForeignKey(fkName2),
j => j.HasOne<TEntity1>().WithMany().HasForeignKey(fkName1),
j => j.ToTable(joinTableName)
);
EF Core 5 Many-to-Many relation - Change the Navigation key
It was achieved by adding the .HasForeignKey() in usingEntity
builder.Entity<Framework>()
.HasMany(x => x.Paths)
.WithMany(x => x.LinkedFrameworks) //Change the name here
.UsingEntity<Dictionary<string, object>>(
"FrameworkPaths",
x => x.HasOne<Path>().WithMany(),
x => x.HasOne<Framework>().WithMany().HasForeignKey("FrameworksId")); //tell the EF about the Foreign Key
EF Core 5.0 Adding many-to-many makes one-to-many unable to determine
Multiple relationships between two entities usually cannot be determined automatically and need configuration. Many things (and especially relationships related) in EF Core can only be configured only with fluent API.
So rather than looking for non existing or non intuitive data annotations, simply configure the desired relationships (at minimum - navigations properties and cardinality) fluently:
modelBuilder.Entity<Notification>(builder =>
{
builder.HasOne(e => e.Creator).WithMany();
builder.HasOne(e => e.Updater).WithMany();
builder.HasMany(e => e.Recipients).WithMany(e => e.Notifications);
});
EntityFramework Core 3.0 Fluent API Many-to-many relationship builder creating extra foreign key columns
EDIT
As per @Flater's comment the reason for this was that the configuration was specified twice thus an extra set of columns was created.
Actually removing the duplex one-to-many Fluent API configuration from the ThreadsParticipants
entity type configuration solves this issue and the extra properties are no longer created. When this portion of the config is removed
EF uses naming convetions to figure the relationship out so there's no need to explicitly state the relationship details.
builder
.HasOne(tp => tp.ThreadModel)
.WithMany()
.HasForeignKey(tp => tp.ThreadModelId)
.IsRequired();
builder
.HasOne(tp => tp.ParticipantModel)
.WithMany()
.HasForeignKey(tp => tp.ParticipantModelId)
.IsRequired();
This leads me to think that there is a property name mismatch that is causing all the trouble.
Related Topics
Does a Locked Object Stay Locked If an Exception Occurs Inside It
Using the C# Dispatcher in Wpf Applications
What Is the Use of the Arraysegment<T> Class
Why Does Wcf Return Myobject[] Instead of List<T> Like I Was Expecting
How to Programmatically Change Active Directory Password
I Need a Event to Detect Internet Connect/Disconnect
Using JSON.Net to Return Actionresult
Proper JSON Serialization in MVC 4
Observablecollection and Threading
Are These Objects's References on the Stack or on the Heap
How to Set Conditional Breakpoints in Visual Studio
Unhandled Exceptions in Backgroundworker
How to Receive Plug & Play Device Notifications Without a Windows Form