防止在 Entity Framework 中自动填充圆形导航属性
Preventing automatic population of circular navigation properties in Entity Framework
我在检索具有循环引用的实体时遇到问题。我的实体导航属性不是延迟加载的,所以我希望它们为 return null 除非明确包含在查询中,但是我发现当两个实体之间存在循环引用时情况并非如此并且相反,递归层次结构是 returned.
例如,假设我们有两个实体 UserEntity
和 PostEntity
。一个UserEntity
可能有很多post,但是一个post必须只有一个UserEntity
。所以,配置如下:
// UserEntity configuration
HasMany(u => u.Posts)
.WithRequired(p => p.User);
如果我在数据库中查询 UserEntity
或 PostEntity
而没有在各自的 Posts
或 User
导航属性上使用 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
});
}
我在检索具有循环引用的实体时遇到问题。我的实体导航属性不是延迟加载的,所以我希望它们为 return null 除非明确包含在查询中,但是我发现当两个实体之间存在循环引用时情况并非如此并且相反,递归层次结构是 returned.
例如,假设我们有两个实体 UserEntity
和 PostEntity
。一个UserEntity
可能有很多post,但是一个post必须只有一个UserEntity
。所以,配置如下:
// UserEntity configuration
HasMany(u => u.Posts)
.WithRequired(p => p.User);
如果我在数据库中查询 UserEntity
或 PostEntity
而没有在各自的 Posts
或 User
导航属性上使用 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
});
}