【问题标题】:Entity Framework Core: Adding an object with a List to a DbContext results in an empty List实体框架核心:将带有列表的对象添加到 DbContext 会导致空列表
【发布时间】:2019-10-23 21:30:46
【问题描述】:

我们正在尝试将实体框架模型保存到数据库中。

在模型中,Track 始终包含一条消息,并且一条消息的重传列表中可能包含任意数量的消息。

保存模型时,我们检查数据库中是否已经存在Track模型;如果没有,我们只需将 Track 添加到 DbContext。

public class Track
{
    [Key]
    public Guid TrackId { get; set; }
    public virtual Message Message { get; set; }
}

public class Message
{
    [Key]
    public Guid MessageId { get; set; }
    public Guid TrackId { get; set; }

    public virtual List<Message> Retransmits { get; set; }
}

public void Save (Track track)
{
    using (var context = new DatabaseContext())
    {
        Track foundTrack = Read(track.TrackId); // Returns a Track if it already exists in the database

        if (foundTrack == null)
        {
            context.Add(track);
        }
        else
        {
            // Update the Track. Not relevant to this question
        }

        context.SaveChanges();
    }
}

// Returns a Track if it exists in the database or null
public Track Read(Guid trackId)
{
    using (var context = new DatabaseContext())
    {
        return context.Tracks
            .Include(t => t.Message)
                .ThenInclude(m => m.Retransmits)
                .FirstOrDefault(t => t.TrackId == trackId);
    }
}

当向 DbContext 实体框架添加 Track 时,将始终清除重传列表。这意味着在调用 DbContext.Add 之前,消息的重传列表中可以包含一条消息,但在调用 DbContext.Add 之后,它会突然变为 0。实体框架正在有效地清除列表,而不是将其添加到数据库中。

我们预计重传列表将被添加到 DbContext 中,并且不会在没有警告的情况下从模型中删除。

在调试过程中,我们发现将重传列表从 List&lt;Message&gt; 更改为 List&lt;AnyOtherObject&gt; 在数据库中返回了预期的结果和行。

似乎因为 Retransmits 列表与它所包含的类的类型相同(即,一条消息可以包含许多消息),实体框架对它的处理方式不同。

我们还尝试在modelBuilder.Entity&lt;Message&gt;().HasMany(m =&gt; m.Retransmits) 没有区别的实体之间配置一对多关系。乃至 modelBuilder.Entity&lt;Message&gt;().HasMany(m =&gt; m.Retransmits).WithOne(m =&gt; m.Parent) 其中 Parent 是对重传消息的包含对象的引用。但是,这导致了 StackOverflow 异常。

Entity Framework 拒绝将列表添加到数据库的原因可能是什么?有什么方法可以配置实体框架以允许消息包含重传列表(属于消息类型)并按预期将其添加到数据库中?还是我们完全做错了什么?

I have attached an image of the behavior in Visual Studio's Debugger.

编辑:我们已经尝试了最新的稳定 Entity Framework Core 版本 2.2.6,以及 Entity Framework Core 版本 3.0.0 - Preview 9。相同的行为存在于两个版本中。

编辑编辑:以下代码初始化模型并应重现问题。

public void ReproduceIssue ()
{
    // Create a Track and Message
    Track track = new Track()
    {
        Message = new Message()
    };

    track.Message.TrackId = track.TrackId;

    // Create a Retransmitted Message
    Message retransmit = new Message()
    {
        TrackId = track.TrackId
    };

    // Add the Retransmitted Message to the List
    track.Message.Retransmits.Add(retransmit);

    Save(track);
}

【问题讨论】:

  • 让我们澄清一下使用场景。 track.Message 是新的,因为 MessageIddefault(Guid)) 并且应该在轨道之前创建,对吗? track.Message.Retransmits 中的 Message 对象怎么样?在调用Add(track) 之前,您可以在图像中展开它们吗?或者更好的是,提供创建Track 对象并调用重现问题的Save 方法的小代码sn-p?
  • @Ivan Track.Message 与 Track 同时创建。没有另一个就不能存在,但重传可能根本不存在。我已经使用设置编辑了原始问题以重现该问题。这里还有一张 Visual Studio 中扩展对象的图像:link
  • 从简单的逻辑/数据库设计角度来看,为什么重传会再次包含整个消息。假设重传只是试图再次传输相同的消息,不应该有一个消息表和一个传输表,并且一个消息可以有零到 n 次传输,而不会一遍又一遍地复制所有消息数据?
  • 这个模型有问题。有流畅的配置吗?无关系配置 EF Core 假定从 TrackMessage 的一对一关系,这可以解释这种行为,因为您将相同的 TrackId 分配给许多消息,这需要一对多关系.
  • 感谢您的所有意见。我们正在做出改变,在一些设计理念上来回反复。我们有一个工作原型,它似乎可以与实体框架一起正常工作。当我们有一些结果时,我会发布答案:)

标签: c# entity-framework-core


【解决方案1】:

我们对模型进行了一些更改。虽然我认为实体框架不应该在不发出警告/异常的情况下丢弃任何信息,但我们确实规避了这个问题。

我想发布代码,但它有点复杂,因为原始问题遗漏了很多代码细节。但是,解决方案基本上是使用连接表配置消息之间的多对多关系。

之后,它的行为与预期一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-14
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 1970-01-01
    • 2018-07-22
    相关资源
    最近更新 更多