pantonis
pantonis

Reputation: 6507

EF Core delete data in many to many relationship

I have the following tables:

public class Team 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<UserTeam> UserTeams { get; set; }
}

public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<UserTeam> UserTeams { get; set; }
}

public class UserTeam
{
    public long UserId { get; set; }
    public User User { get; set; }

    public long TeamId { get; set; }
    public Team Team { get; set; }
}

the many to many relationship is defined in context:

modelBuilder.Entity<UserTeam>()
    .HasKey(bc => new { bc.UserId, bc.TeamId });

modelBuilder.Entity<UserTeam>()
    .HasOne(bc => bc.User)
    .WithMany(b => b.UserTeams)
    .HasForeignKey(bc => bc.UserId)
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<UserTeam>()
    .HasOne(bc => bc.Team)
    .WithMany(c => c.UserTeams)
    .HasForeignKey(bc => bc.TeamId)
    .OnDelete(DeleteBehavior.Restrict);

I am trying to delete some users from a team with the following code:

public async Task RemoveUsersFromTeam(int teamId, List<long> users)
{
    Team existingTeam = await dbContext.Team.Include(x => x.UserTeams).FirstOrDefaultAsync(x => x.Id == teamId);
    foreach (var user in users)
    {
        existingTeam.UserTeams.Remove(new UserTeam { UserId = user });
    }

    await dbContext.SaveAsync();
}

but this query is not deleting the users that I pass. Anyone knows why this happens?

Upvotes: 1

Views: 2128

Answers (1)

Dave Williams
Dave Williams

Reputation: 2246

You can delete objects by Id using the new and attach method, or by passing the actual entity.

Passing Entity

public async Task RemoveUsersFromTeam(int teamId, List<long> userIds)
{
    Team existingTeam = await dbContext.Team.Include(x => x.UserTeams).FirstOrDefaultAsync(x => x.Id == teamId);
    foreach (var userId in userIds)
    {
        var userTeam = existingTeam.UserTeams.FirstOrDefault(x => x.UserId == userId);
        if(userTeam != null)
        {
            existingTeam.UserTeams.Remove(userTeam);
        }
    }

    await dbContext.SaveAsync();
}

Delete By Id

public async Task RemoveUsersFromTeam(int teamId, List<long> userIds)
{
    Team existingTeam = await dbContext.Team.FirstOrDefaultAsync(x => x.Id == teamId);
    foreach (var userId in userIds)
    {
        var userTeam = new UserTeam { UserId = userId });
        dbContext.UserTeams.Attach(userTeam);
        existingTeam.UserTeams.Remove(userTeam);
    }

    await dbContext.SaveAsync();
}

Option two does not require selecting UserTeams from the database and will be slightly more efficient in that respect. However option one may be more understandable. You should choose which best fits your situation.

Personally I prefer option two, as include will select the whole entity and in some cases that could be a lot more than necessary.

Upvotes: 2

Related Questions