Home > Blockchain >  How do I create relationship between two already existing database entities? (many-to-many)
How do I create relationship between two already existing database entities? (many-to-many)

Time:12-27

So, I'm getting this error:

InvalidOperationException: The instance of entity type 'PlaylistSong' cannot be tracked because another instance with the same key value for {'PlaylistId', 'SongId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

Here:

public async Task AddSongToPlaylist(int playlistId, int songId)
        {
            var song = await _dbContext.Songs.Where(s => s.Id == songId).FirstOrDefaultAsync();
            var playlist = await _dbContext.Playlists.Include(p => p.Songs).Where(p => p.Id == playlistId).FirstOrDefaultAsync();


          

            _dbContext.PlaylistSongs.Add(new PlaylistSong
            {
                Playlist = playlist,
                Song = song,
            });

            _dbContext.SaveChanges();
        }

PlaylistSongs being a join table.

The two domain entities look like this:

 public class Playlist
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ICollection<PlaylistSong> Songs { get; set; }
        public ICollection<RequestedSong>? RequestedSongs { get; set; }
        public bool IsLive { get; set; }
    }
 public class Song
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Artist { get; set; }
        public int Duration { get; set; }
        public int? CorePlaylistId { get; set; }
        public CorePlaylist? CorePlaylist { get; set; }
        public ICollection<PlaylistSong> Playlists { get; set; }
        public ICollection<RequestedSong> RequestedSongs { get; set; }
    }

Here's the joint table class:

 public class PlaylistSong
    {
        public int PlaylistId { get; set; }
        public Playlist? Playlist { get; set; }
        public int? SongId { get; set; }
        public Song? Song { get; set; }
    }

And here's the SongConfiguraiton:

 public class SongConfiguration : IEntityTypeConfiguration<Song>
    {
        public void Configure(EntityTypeBuilder<Song> builder)
        {
            builder.ToTable("Songs");
            builder.HasKey(s => s.Id);
            builder.Property(s => s.Title).IsRequired();
            builder.Property(s => s.Artist).IsRequired();
            builder.Property(s => s.Duration).IsRequired();
            builder.HasOne(s => s.CorePlaylist)
                .WithMany(cp => cp.Songs)
                .HasForeignKey(s => s.CorePlaylistId);
            builder.HasMany(s => s.RequestedSongs)
                .WithOne(rs => rs.Song)
                .HasForeignKey(rs => rs.SongId); 

        }
    }

And here's the playlistconfiguration:

 public class PlaylistConfiguration : IEntityTypeConfiguration<Playlist>
    {
        public void Configure(EntityTypeBuilder<Playlist> builder)
        {
            builder.ToTable("Playlists");
            builder.HasKey(p => p.Id);
            builder.Property(p => p.Name).IsRequired();

            builder.HasMany(cp => cp.RequestedSongs)
                .WithOne(s => s.Playlist);
        }
    }

and here's the joint table configuration:

   protected override void OnModelCreating(ModelBuilder builder)
        {
            // Configure joint entity

            builder.Entity<PlaylistSong>()
        .HasKey(ps => new { ps.PlaylistId, ps.SongId });
            builder.Entity<PlaylistSong>()
                .HasOne(ps => ps.Playlist)
                .WithMany(p => p.PlaylistSongs)
                .HasForeignKey(ps => ps.PlaylistId);
            builder.Entity<PlaylistSong>()
                .HasOne(ps => ps.Song)
                .WithMany(s => s.Playlists)
                .HasForeignKey(ps => ps.SongId);

            base.OnModelCreating(builder);

            builder.ApplyConfigurationsFromAssembly(typeof(Song).Assembly);
        }
    }

And I'm guessing it's because I'm fetching two existing entities from the db (that are already being tracked) and then inserting them into this join table. What I want to do is, I've created a Playlist... I've created a Song. They have a many to many relationship configured. Now, I want to create the relationship (i.d. add this specific song to that specific Playlist).

I thought the above code would do the trick, but it seems to be inserting a duplicate, which is why I'm getting the error, right?

What options do I have? How do I insert into a many to many relationship when the two entities already exist in the database?

CodePudding user response:

You need a resolution entity. A song may be entered on many playlists, and a playlist may contain many songs - so you need a table 'playlist-song' which defines the songs in any specific playlist.

Something like;

public class PLaylist-Song
{
    public int PlaylistId { get; set; }
    public int SongId { get; set; }
}

CodePudding user response:

This error "The instance of entity type 'PlaylistSong' cannot be tracked because another instance with the same key value for {'PlaylistId', 'SongId'} is already being tracked."

Means exactly what it says. You've already got that PlaylistSong in the DbContext's change tracker. Probably because you loaded it when you loaded the Playlist and Included its songs. So you should examine the Playlist to see if it already contans the requested song.

If you just want to add a PlaylistSong and save it, you don't have to fetch the related entities at all.

You can simply add it like this

public async Task AddSongToPlaylist(int playlistId, int songId)
{
   
    _dbContext.PlaylistSongs.Add(new PlaylistSong
    {
        PlaylistId = playlistId,
        SongId = songId,
    });

    _dbContext.SaveChanges();
}
  • Related