防止在 Entity Framework 中自动填充圆形导航属性

Preventing automatic population of circular navigation properties in Entity Framework

我在检索具有循环引用的实体时遇到问题。我的实体导航属性不是延迟加载的,所以我希望它们为 return null 除非明确包含在查询中,但是我发现当两个实体之间存在循环引用时情况并非如此并且相反,递归层次结构是 returned.

例如,假设我们有两个实体 UserEntityPostEntity。一个UserEntity可能有很多post,但是一个post必须只有一个UserEntity。所以,配置如下:

// UserEntity configuration
HasMany(u => u.Posts)
  .WithRequired(p => p.User);

如果我在数据库中查询 UserEntityPostEntity 而没有在各自的 PostsUser 导航属性上使用 Include(),则导航属性如预期的那样为空。

但是,如果我查询 UserEntity 并包含它的 PostEntity,循环层次结构是 returned,即使我从未请求 PostEntity.User导航 属性 填充:

using (var db = new MyDbContext())
{
  var user = await db.Users
    .Include(u => u.Posts)
    .SingleOrDefaultAsync(u => u.ID == 0);

  // The [UserEntity] contains a list of [PostEntitiy]s
  //   each with the [UserEntity] each with a list of [PostEntitiy]s...
  //     and so on.
}

这不是太麻烦,但是当检索到 PostEntity 的列表并包含它们的 UserEntity 时,事情变得很奇怪:

using (var db = new MyDbContext())
{
  var posts = await db.Posts
    .Include(p => p.User)
    .SingleOrDefaultAsync(p => p.ID == 0);

  // This throws a [System.InvalidOperationException]:
  // "Sequence contains more than one element"
  // How can this be? The ID is unique.

  var posts = await db.Posts
    .Include(p => p.User)
    .Where(p => p.ID == 0)
    .ToListAsync();

  // This returns several copies of the PostEntity
  //   which should be unique in the database. Is
  //   this a side effect of the circular reference?
}

显然,去掉循环引用可以解决这个问题,但如果可能的话,保留它是有益的有几个原因。 为什么 EntityFramework return 使用此循环层次结构,尽管 Include() 只请求了一个单一的单向关系,为什么这会导致多个 PostEntity 被 return 编辑时他们的 UserEntity 被包括在内?

您可以尝试将您的实体投影到 DTO 中来解决这个问题。将 projection 用于某些没有此类引用的类型并避免异常。

换句话说,只从 EF 模型中获取您需要的属性,而不是添加嵌套复杂类型的字段,这些字段也具有指向同一对象的属性,从而创建循环引用

using (var db = new MyDbContext())
{
    var posts = db.Posts
        .Include(p => p.User)
        .Where(p => p.ID == 0)
        .Select(p => new PostsDTO
        {
            Name = p.Name,
            Username = p.User.Username
        });
}