I have what appears to be a textbook example of an indirect many-to-many relationship.
I can retrieve and add new entities to the relationship but cannot remove them.
From all the examples I've followed it just doesn't work.
Data models:
public class Job
{
public int JobId { get; set; }
public int AccountId { get; set; }
public string Name { get; set; }
public virtual Account PrimaryAccount { get; set; }
public virtual ICollection<AccountAlert>? AccountAlerts { get; set; }
}
public class Account
{
public int AccountId { get; set; }
public string Name { get; set; }
public virtual ICollection<AccountAlert>? AccountAlerts { get; set; }
}
public class AccountAlert
{
public int AccountId { get; set; }
public int JobId { get; set; }
public virtual Account Account { get; set; }
public virtual Job Job { get; set; }
}
DbContext
:
public class DataContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<AccountAlert> AccountAlerts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSnakeCaseNamingConvention();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>().ToTable("account");
modelBuilder.Entity<Job>().ToTable("job");
modelBuilder.Entity<AccountAlert>().ToTable("account_alert").HasKey(x => new { x.JobId, x.AccountId });
}
}
Remove code:
var job = _dataContext.Set<Job>()
.Where(x => x.JobId == jobId)
.Include("AccountAlerts")
.Include("AccountAlerts.Account")
.AsNotTracked()
.Single();
foreach(var alert in job.AccountAlerts)
{
// Doesn't work
job.AccountAlerts.Remove(alert);
// Doesn't work
job.AccountAlerts.Remove(new AccountAlert { JobId = alert.JobId, AccountId = alert.AccountId });
// Doesn't work
job.AccountAlerts.Remove(new AccountAlert { AccountId = alert.AccountId });
}
await _dataContext.SaveChangesAsync();
Another solution to the problem besides the one suggested by Steve Py(which should work) is to delete directly from the mapping table:
// you can take accountAlerts by AccountId or JobId
var accountAlerts = await _dataContext.AccountAlerts
.Where(x => accountIds.Contains(x.AccountId))
.ToListAsync();
context2.RemoveRange(accountAlerts);
await context2.SaveChangesAsync();
Or if you want to clear all alerts in the job just use Clear()
var job = await _dataContext.Set<Job>()
.Where(x => x.JobId == jobId)
.Include(x => x.AccountAlerts)
.FirstOrDefaultAsync();
if (job is not null)
{
job.AccountAlerts.Clear();
await context2.SaveChangesAsync();
}