实体框架模型中的集合未更新

Posted

技术标签:

【中文标题】实体框架模型中的集合未更新【英文标题】:Collection in entity framework model is not updating 【发布时间】:2019-07-03 07:05:10 【问题描述】:

我有几个简单的模型。一个 PlaylistEntry 模型和一个包含 PlaylistEntry 模型集合的播放列表模型。

播放列表:

    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;
        
    

播放列表条目

    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; 
    

当我将 PlaylistEntry 添加到处于分离状态的播放列表时,我希望在附加并保存更改时,对 PlaylistEntries 列表的更改也将被保存。他们不保存,列表为空。

这是我的代码:

    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();
    

最后一次 ToList 调用返回我创建的播放列表,但其中的 PlaylistEntries 集合为空。

谁能发现我做错了什么或提出不同的解决方案?我已经花了大约 7 个小时来解决这个问题,但我终其一生都无法弄清楚。

【问题讨论】:

这只是一个猜测,但您检查过Entry 中的PlaylistEntry 对象吗? 我想知道您是否需要将这些对象附加到 context.PlaylistEntries,假设存在。 【参考方案1】:

您缺少的功能是上下文缓存的工作方式。当您将实体(手动或通过从数据库请求实体)附加到上下文时,实体框架监视以进行更改并跟踪这些更改。

您已将对象添加到实体的属性中,然后将其添加到上下文中。 Entity Framework 现在正在观察它的变化以做任何额外的工作。以这种方式添加的所有 对象都标记为未更改。标记父 已更改 不会将 导航属性 标记为 已更改

您可以将实体添加到上下文中的属性:

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 在附加父对象后标记两个对象条目。

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();

在这两种情况下,Entity Framework 现在都知道当SaveChanges() 发生时在数据库中做什么。

DotNetFiddle

【讨论】:

非常感谢!两种解决方案都有效。我决定采用第一种方法,因为它似乎使事情变得不那么复杂。对于第二种解决方案,我不得不稍微修改代码以处理多个新条目。更新了帖子 对于您的第一种方法,有没有办法让 PlaylistEntryId 自动生成?如果我没有明确提供 ID,我会收到异常 A null store-generated value was returned for a non-nullable member 'PlaylistEntryId' of type 'MusicPlayer.Data.DAL.PlaylistEntry'. 这个问题的答案是我的架构已经过时了。我删除并重新创建了解决问题的数据库【参考方案2】:

每当您在仅添加PlayList 而没有PlaylistEntries 之后调用context.SaveChanges(); 时,实体框架都会将命令发送到数据库并重置上下文状态。所以在那之后将PlaylistEntries 添加到PlayList 不会产生任何效果。要插入 PlaylistEntriesPlaylist,请编写如下代码:

var playlist = new Playlist  Name = "My first playlist" ;
using (var context = new MusicPlayerContext(stringBuilder))

    playlist.PlaylistEntries.Add(new PlaylistEntry  FilePath = "lkdfj", PlaylistId = playlist.PlaylistId ); // Even you don't need to specify `PlaylistId` here. EF can map it.
    context.Playlists.Add(playlist);
    context.SaveChanges();

希望这对你有用。

【讨论】:

【参考方案3】:

我决定从上下文中向列表中添加新条目,就像其他答案所建议的那样。如果有人仍然想知道如何使其在分离状态下工作,请按以下方法操作:

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();

context.Playlists.Add(playlist) 实际上并没有添加新的播放列表。据我了解,它附加了播放列表和所有新条目。如果我不使用 Add,我会收到一个错误,因为新条目的 PlaylistEntryID 默认都设置为 0。

【讨论】:

以上是关于实体框架模型中的集合未更新的主要内容,如果未能解决你的问题,请参考以下文章

使用实体框架核心模型时 WPF 数据网格未更新

从实体框架中的集合加载相关实体

您是不是需要在 .Add() 之前加载实体框架集合的内容?

将关系映射到实体框架中的抽象集合

实体框架-关系未更新

学说刷新不更新相关的集合实体