Collection in entity framework model is not updating

c# database entity-framework entity-framework-6

Question

I have a couple of simple models. An PlaylistEntry model and a Playlist model which contains a collection of PlaylistEntry models.

Playlist:

    public class Playlist
    {
        private ICollection<PlaylistEntry> playlistEntries;

        public int PlaylistId { get; set; }

        public string Name { get; set; }

        public virtual ICollection<PlaylistEntry> PlaylistEntries
        {
            get => this.playlistEntries ?? (this.playlistEntries = new HashSet<PlaylistEntry>());
            set => this.playlistEntries = value;
        }
    }

PlaylistEntry

    public class PlaylistEntry
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int PlaylistEntryId { get; set; }

        public string FilePath { get; set; }

        [ForeignKey("Playlist")]
        public int PlaylistId { get; set; }

        [Required]
        public Playlist Playlist { get; set; }
    }

When I add a PlaylistEntry to a Playlist in a detached state, I expect that when I attach and save changes, the changes to the PlaylistEntries list will also be saved. They don't save and the list is empty.

Here's my code:

    var playlist = new Playlist { Name = "My first playlist" };
    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Add(playlist);
        context.SaveChanges();
    }

    playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "lkdfj", PlaylistId = playlist.PlaylistId });

    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Attach(playlist);
        context.Entry(playlist).State = EntityState.Modified;
        context.SaveChanges();
    }

    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Include(x => x.PlaylistEntries).ToList();
    }

That last ToList call returns the playlist I create but the PlaylistEntries collection on it is empty.

Can anyone spot what I'm doing wrong or suggest a different solution please? I've spent about 7 hours on this and can't for the life of me figure it out.

1
5
2/9/2019 12:33:38 PM

Accepted Answer

The feature you're missing is how the Context Cache works. When you attach an entity (either manually or by requesting an entity from the database) to the context, it's watched by entity framework for changes and tracking those changes.

You've added an object to an entitiy's propety, then added it to the context. Entity Framework is now watching it for changes to do any additional work. All objects added this way are marked as unchanged. Marking the parent changed does not mark the navigation properties as changed.

You can either add the entity to the property inside the context:

using (var context = new MusicPlayerContext())
{
    Console.WriteLine("Save PlayList 2 (in context add)");
    context.Playlists.Attach(playlist3);
    playlist3.PlaylistEntries.Add(new PlaylistEntry { 
      FilePath = "Entry3", 
      PlaylistId = playlist3.PlaylistId, PlaylistEntryId = 3 
    });
    context.SaveChanges();
}

... OR mark both objects entries after attaching the parent.

using (var context = new MusicPlayerContext())
{
    Console.WriteLine("Save PlayList 2 (full attach)");
    context.Playlists.Attach(playlist2);
    context.Entry(playlist2).State = EntityState.Modified;
    context.Entry(playlist2.PlaylistEntries.First()).State = EntityState.Added;
    context.SaveChanges();
}

In both cases Entity Framework now knows what to do in the DB when SaveChanges() occurs.

DotNetFiddle

3
2/9/2019 11:26:59 AM

Popular Answer

I've decided to add new entries to the list from within the context like the other answers have suggested. If anyone still wants to know how to get this working in a detached state, here's how:

var playlist = new Playlist { Name = "My first playlist" };

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Add(playlist);
}

playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "Song7" });
playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "Song8" });

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Add(playlist);
    context.Entry(playlist).State = EntityState.Modified;

    foreach (var entry in playlist.PlaylistEntries)
    {
        context.Entry(entry).State = entry.PlaylistEntryId == 0 ? EntityState.Added : EntityState.Modified;
    }

    context.SaveChanges();
}

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Include(x => x.PlaylistEntries).ToList();
}

The context.Playlists.Add(playlist) doesn't actually add a new playlist. From what I understand, it attaches the playlist and all of the new entries on it. If I don't use Add, I get an error because the PlaylistEntryID of the new entries are all set to 0 by default.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow