Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collection in entity framework model is not updating

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.

like image 548
tamj0rd2 Avatar asked Feb 09 '19 02:02

tamj0rd2


1 Answers

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

like image 56
Erik Philips Avatar answered Oct 04 '22 07:10

Erik Philips