Entity Framework 核心:SQLite 的所有其他结果都缺少相关数据

Entity Framework Core: related data missing for every other result with SQLite

我在从数据库中检索数据时遇到了一个奇怪的问题。这是一个从 .NET Core 1.0 升级到 .NET Core 2.1 的项目。之前一切正常,但升级在加载相关数据时有一些奇怪的副作用。使用的确切版本是 2.1 for .NETCore.app; 2.1.4 用于 AspNetCore,2.1.4 用于 EntityFrameworkCore(.Sqlite)。

问题可以用以下模型来表达。插入后,首先添加一个 Match,但没有相关的 Result 实体。然后,创建每个 Result,最后用这些实例再次更新 Match。这就是 Match.

中可空 ID 字段的原因
public class Result
{
  public int Id { get; set; }
  public int MatchId { get; set; }
  public Match Match { get; set; }
}

public class Match
{
  public int Id { get; set; }
  public int? HomeId { get; set; }
  public int? AwayId { get; set; }
  public Result Home { get; set; }
  public Result Away { get; set; }
}

当我检索数据(例如,context.Results.Include(r => r.Match))并检查结果时,每个 other 结果都遗漏了相关数据。这似乎只发生在这些表上,所以我的猜测是由于模型的双向性而误入歧途。

检查员显示如下结果。应该注意的是,两个连续结果的集合总是指向相同的 Match.

results - [0] Id = 1000 Match = Null - [1] Id = 1001 Match = <Match object> - [2] Id = 1002 Match = Null - [3] Id = 1003 Match = <Match object>

等等。我也无法将数据插入到这些表中(弹出类似问题),但此时让我们将其排除在这个问题的范围之外。

回答我自己的问题:问题出在关系映射中。有 several related questions 恰好与我的相匹配,但建议通常在于创建额外的字段以更好地描述映射。由于不会更改数据库模式,因此这不是一个选项。

附带说明一下,这个问题与 SQLite 没有太大关系。然而奇怪的是,事情曾经按照问题中的描述工作。我没有调查是什么原因造成的,但看起来 Entity Framework 的 SQLite 二进制文件处理这种情况的方式不同。并不是说这是一个错误,但作为版本之间的主要区别,它可能至少与此有关。

也就是说,解决方案。 ModelBuilder 最初有以下定义:

modelBuilder.Entity<Match>()
  .HasOne(m => m.Home)
  .WithMany()
  .HasForeignKey(m => m.HomeId);

modelBuilder.Entity<Match>()
  .HasOne(m => m.Away)
  .WithMany()
  .HasForeignKey(m => m.AwayId);

modelBuilder.Entity<Result>()
  .HasOne(r => r.Match)
  .WithOne()
  .HasForeignKey<Result>(r => r.MatchId);

其中一个问题是来自 MatchWithMany 映射不正确。将其更改为 WithOne - 尽管更正确 - 对问题没有任何影响。它已通过删除从 ResultMatch 的关系映射得到修复。显然,这默默地映射了反向关系,选择两个关系之一朝向 Result。因为(我假设)Entity Framework 会在指定一端后处理反向关系,所以可以(应该)将其排除以修复问题并保留所有导航。

为了完整性,最后的ModelBuilder定义:

modelBuilder.Entity<Match>()
  .HasOne(m => m.Home)
  .WithOne()
  .HasForeignKey(m => m.HomeId);

modelBuilder.Entity<Match>()
  .HasOne(m => m.Away)
  .WithOne()
  .HasForeignKey(m => m.AwayId);