我在Include
的查询中包含了导航属性,因此以后不会加载延迟。但是,当我创建一个带有Select
投影的匿名包装对象时,它不起作用。
让我展示一下简化的例子。 实体:
public class UserEntity {
public string Name {get;set;}
public virtual ICollection<UserEntity> Friends { get; set; }
}
查询:
var entry = _dbCtx
.Users
.Include(x => x.Friends)
// Select here is simplified, but it shows the wrapping
.Select(user => new {
User = user
})
.First();
// Here we have additional lazy loaded DB call
var friends = entry.User.Friends.Select(x => x.Name).ToList();
我也从生成的SQL中看到,不包括导航属性:
SELECT
[Limit1].[Name] AS [Name],
FROM ( SELECT TOP (1)
[Extent1].[Name] AS [Name]
FROM [dbo].[Users] AS [Extent1]
) AS [Limit1]
在这种情况下是否可以Include
导航属性Friends
,以便User
能够在不延迟加载的情况下获取数据?
我期待这也能奏效:
var entry = _dbCtx
.Users
.Select(user => new {
User = user
})
.Include(x => x.User.Friends)
.First();
但是得到例外:
InvalidOperationException:查询的结果类型既不是EntityType,也不是具有实体元素类型的CollectionType。只能为具有这些结果类型之一的查询指定包含路径。
我遇到了一些变通方法,但它们有些棘手:
在Select
:
var entry = _dbCtx
.Users
.Select(user => new {
User = user,
UsersFriends = user.Friends
})
.First();
// manually copy the navigation property
entry.User.Friends = user.UsersFriends;
// Now we don't have any addition queries
var friends = entry.User.Friends.Select(x => x.Name).ToList();
将用户映射到数据库级别的匿名对象,然后将属性映射到C#中的UserEntity
。
var entry = _dbCtx
.Users
.Select(user => new {
User = new {
Name = user.Name,
Friends = user.Friends
}
})
.Take(1)
// Fetch the DB
.ToList()
.Select(x => new {
User = new UserEntity {
Name = x.Name,
Friends = x.Friends
}
})
.First();
// Now we don't have any addition queries
var friends = entry.User.Friends.Select(x => x.Name).ToList();
现在,LEFT OUTER JOIN
有一个Friends
,但两种解决方法都不太好:
1)附加属性和副本不是一种干净的方式。
2)我的UserEntity还有更多其他属性。另外,每次添加新属性时,我们都应该在这里修改选择器。
是否有某种方法可以实现导航属性,包括第一个样本?
感谢您阅读,我希望有人知道这一点。
修改
我将扩展实体和查询以显示真实的用例。
实体
public class UserEntity {
public string Name {get;set;}
public int Score {get;set;}
public virtual ICollection<UserEntity> Friends { get; set; }
}
查询
var entry = _dbCtx
.Users
.Include(x => x.Friends)
.Select(user => new {
User = user,
Position = _dbCtx.Users.Count(y => y.Score > user.Score)
})
.First();
答案 0 :(得分:0)
不是_why_的答案,但想要更好的代码格式化......
我真的很惊讶它的工作方式。也许EF检测到您没有在投影中直接使用Friends
属性,因此忽略了它。如果您封装了EF查询的 对象
var entry = _dbCtx
.Users
.Include(x => x.Friends)
.Take(1); // replicate "First" inside the EF query to reduce traffic
.AsEnumerable() // shift to linq-to-objects
// Select here is simplified, but it shows the wrapping
.Select(user => new {
User = user
})
.First()