I am trying to set up a many-to-many with additional properties in EF Core 2.2. After reading, it looks like I need to make my join table a first class citizen. I have done that, but the schema it is generating is not quite right. It's adding an unwanted shadow property called "UserId1".
Here is the domain model:
User.cs
public class User : IdentityUser<long> {
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<UserJobRecommendations> RecommendedJobs { get; protected set;
}
Job.cs
public class Job : BaseEntity<Job> {
public long Id { get; protected set; }
public string Name {get ; protected set;}
public IList<UserJobRecommendations> RecommendedTo { get; protected set; }
}
UserJobRecommendations.cs
public class UserJobRecommendations {
public Job Job { get; protected set; }
public long JobId { get; protected set; }
public User User { get; protected set; }
public long UserId { get; protected set; }
public long RecommendedById { get; protected set; }
public User RecommendedBy { get; protected set; }
}
Finally, here is my context:
public class MyContext : IdentityDbContext<User, IdentityRole<long>, long> {
public DbSet<Job> Jobs { get; set; }
public DbSet<UserJobRecommendations> UserJobRecommendations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<UserJobRecommendations>().HasKey(k => new { k.UserId, k.JobId });
modelBuilder.Entity<UserJobRecommendations>().HasOne(x => x.User).WithMany(x=>x.RecommendedJobs);
modelBuilder.Entity<UserJobRecommendations>().HasOne(x => x.RecommendedBy);
modelBuilder.Entity<User>().HasMany(x => x.RecommendedJobs);
modelBuilder.Entity<Job>().HasMany(x => x.RecommendedTo);
modelBuilder.Seed();
base.OnModelCreating(modelBuilder);
}
Here is a sample of the schema that it is creating for this. I don't want a userId1
property. The table should have 3 properties. JobId
, UserId
, RecommendedById
.
migrationBuilder.CreateTable(
name: "UserJobRecommendations",
columns: table => new
{
JobId = table.Column<long>(nullable: false),
UserId = table.Column<long>(nullable: false),
UserId1 = table.Column<long>(nullable: true),
RecommendedById = table.Column<long>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserJobRecommendations", x => new { x.UserId, x.JobId });
table.ForeignKey(
name: "FK_UserJobRecommendations_Jobs_JobId",
column: x => x.JobId,
principalTable: "Jobs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserJobRecommendations_AspNetUsers_RecommendedById",
column: x => x.RecommendedById,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserJobRecommendations_AspNetUsers_UserId1",
column: x => x.UserId1,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Problem is in your Many-to-Many
entity configuration. Your Many-to-Many
configuration between User
and Job
is not written properly.
Write your entity configuration as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Many-to-Many configuration between User and Job
modelBuilder.Entity<UserJobRecommendations>().HasKey(ujr => new { ujr.UserId, ujr.JobId });
modelBuilder.Entity<UserJobRecommendations>().HasOne(ujr => ujr.User).WithMany(u=>u.RecommendedJobs)
.HasForeignKey(ujr => ujr.UserId).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<UserJobRecommendations>().HasOne(ujr => ujr.Job).WithMany(j=>j.RecommendedJobs)
.HasForeignKey(ujr => ujr.JobId).OnDelete(DeleteBehavior.Restrict);
// RecommendedBy ForeignKey Configuraiton
modelBuilder.Entity<UserJobRecommendations>().HasOne(ujr => ujr.RecommendedBy).WithMany().HasForeignKey(ujr => ujr.RecommendedById);
modelBuilder.Seed();
}
Now on Migration everything should generate as expected!