防止在Entity Framework中自动填充循环导航属性

时间:2016-01-14 23:02:55

标签: c# entity-framework linq circular-reference

检索具有循环引用的实体时出现问题。我的实体导航属性不是延迟加载的,所以我希望它们返回null,除非特别包含在查询中,但是我发现当两个实体之间存在循环引用时不是这种情况而是递归层次结构归还。

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

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

如果我在相应的UserEntityPostEntity导航属性上查询数据库中的Include()Posts而未使用User,则导航属性按预期为空。

但是,如果我查询UserEntity并包含其PostEntity,则会返回循环层次结构,即使我从未请求填充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返回此循环层次结构,尽管只有Include()请求单个单向关系,为什么这会导致在PostEntity时返回多个UserEntity包括在内?

1 个答案:

答案 0 :(得分:2)

您可以尝试将您的实体投影到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
        });
}