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