It's failing because you're trying to add a new row to the table with the same ID as an existing row. It also sort of looks like you have a unique constraint on the DB table such that each client can be associated with the same channel once.
Where things are going wrong is in the fact that you're not actually tracking the object in the DB, you're creating a new one. You actually do this twice in the original post. If you intend to update data in the DB using EF then you should never call AsNoTracking
on the object when you get it. The net effect of this call is that EF doesn't track changes to the object. Later you assign a new value to the client which ensures that it is not the original object EF is tracking.
Looking at your Github sample things are more complex.
- You're still doing a
AsNoTracking
which you shouldn't do for an update. - When it comes time to add channels you're fetching the channels from the DB and adding them to the list of channels.
Try this in your MapPut
.
var localClient = await db.Clients.Include(x => x.Channels).FirstOrDefaultAsync(x => x.Id == id);
//Remove the old ones
localClient.Channels.RemoveAll(x => channelIdsToRemove.Contains(x.Id));
//Add the new ones, note that I assume you've already handled looking for dups
//Don't need to actually fetch the channels from the DB first since EF should
//be able to figure out that the channel already exists in the DB based upon its unique ID
localCLient.Channels.AddRange(channelIdsToAdd.Select(x => new Channel() { Id = x }));
await db.SaveChangesAsync();