Azhari
Azhari

Reputation: 642

Entity Framework Core 5.0 - Many to many with additional field in mapping table

I'm using EF Core 5.0 and I'm attempting to create a many-to-many relationship:

public class User
{
    public int Id { get; set; }
    public ICollection<UserItem> UserItems { get; set; }
}

public class Item
{
    public int Id { get; set; }
    public ICollection<UserItem> UserItems { get; set; }
}

public class UserItem
{
    public int Id { get; set; }

    public int UserId { get; set; }
    public User User { get; set; }

    public int ItemId { get; set; }
    public Item Item { get; set; }

    public int Quantity { get; set; }
}

As you can see, the UserItem class has an additional property called Quantity, I was wondering if this is the correct approach, and how would I map all this in the DBContext class?

Upvotes: 0

Views: 1339

Answers (2)

Nannanas
Nannanas

Reputation: 631

Yes this is correct! But I would recommend removing the Id property from UserItem and configure a composite key using -

modelBuilder.Entity<UserItem>()
            .HasKey(userItem => new { userItem.UserId, userItem.ItemId });

in the DbContext so it is ensured that only one single UserItem for the same User/Item combination can exist (as I assume you want to use the Quantity property for representing "multiple" relationships between the same two items).

EF Core correctly sets up the relationships and foreign keys for you but you need to tell him which part of the relationship is the principal part.

If you want UserItem to be cascaded when the Item is deleted but restrict the deletion of the User when an UserItem is still referencing said User then add -

modelBuilder.Entity<UserItem>()
            .HasOne(userItem => userItem.User)
            .WithMany(user => user.UserItems)
            .OnDelete(DeleteBehavior.Restrict);

If you want UserItem to be cascaded when the User is deleted but restrict the deletion of the Item when an UserItem is still referencing said Item then add -

modelBuilder.Entity<UserItem>()
            .HasOne(userItem => userItem.Item)
            .WithMany(item => item .UserItems)
            .OnDelete(DeleteBehavior.Restrict);

And if you want to restrict the deletion of User and Item if they are referenced by a UserItem add both of them. Notice that you will need at least one of them because otherwise you will get an exception when adding the migration because you have multiple cascade paths for UserItem.

Upvotes: 3

Serge
Serge

Reputation: 43969

Everything is fine, maybe you will have add to your dbContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
          
            modelBuilder.Entity<UserItem>(entity =>
            {
                entity.HasOne(d => d.User)
                    .WithMany(p => p.UserItems)
                    .HasForeignKey(d => d.UserId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    

                entity.HasOne(d => d.Item)
                    .WithMany(p => p.UserItems)
                    .HasForeignKey(d => d.ItemId)
                    
            });

            OnModelCreatingPartial(modelBuilder);
        }

but if you use Net5 you are allowed to add a list of items:

public class User
{
    public int Id { get; set; }
    public ICollection<UserItem> UserItems { get; set; }
     public ICollection<Items> Items { get; set; }
}

public class Item
{
    public int Id { get; set; }
    public ICollection<UserItem> UserItems { get; set; }
    public ICollection<Users> Users { get; set; }

}

Upvotes: 1

Related Questions