EF 核心 2.1 使用投影
EF core 2.1 using take in projection
我想在投影中使用 Take() 但我不想生成 N+1 查询,此外在投影中没有 Take() 我面临性能问题。
我将 Take() 与 EF6 一起使用,但我在 EF Core 上遇到了 N+1 问题。
示例投影:
source.Select(post => new PostProject
{
PostDisableCoins = post.PostDisableCoins
.OrderBy(x=>x.CoinAmount)
.Take(3)
.ToList(),
WarStartTime = post.WarStartTime,
WarEndTime = post.WarEndTime,
WarWinner = post.WarWinner,
WarDeclarer = post.WarDeclarer
});
我想要没有 N+1 的 Take(3),有什么建议吗?!?
请注意有关 EF core 2.1 new features:
的文档
We have improved our query translation to avoid executing "N + 1" SQL
queries in many common scenarios in which the usage of a navigation
property in the projection leads to joining data from the root query
with data from a correlated subquery. The optimization requires
buffering the results from the subquery, and we require that you
modify the query to opt-in the new behavior.
例如:
var query = context.Customers.Select(
c => c.Orders.Where(o => o.Amount > 100).Select(o => o.Amount).ToList());
注意包含 .ToList()
的位置。
您需要相应地修改您的投影查询,以启用优化功能。
你的情况可能是:
source.Select(post => new PostProject
{
PostDisableCoins = post.PostDisableCoins
.Select(x => x.OrderBy(x=>x.CoinAmount))
.Select(x => x)
.Take(3)
.ToList(),
WarStartTime = post.WarStartTime,
WarEndTime = post.WarEndTime,
WarWinner = post.WarWinner,
WarDeclarer = post.WarDeclarer
});
这是 EF Core 2.1 实现缺陷。以下是解决方法,但仅当您确实遇到性能问题时才使用它,因为它需要打破导航 属性 连接抽象并使用手动连接,我总是说不应该与 EF (Core) 一起使用。如果用于投影多个集合或作为更复杂查询的一部分,也可能不起作用。
它需要使用横向连接将集合导航 属性 post.PostDisableCoins
的用法替换为 SelectMany
并隐藏 OrderBy
/ Take
运算符(更新使用正确的类型和 PK/FK 个名称):
var postDisableCoinsQuery = source.SelectMany(p =>
db.Set<PostDisableCoin>()
.Where(c => c.PostId == p.Id)
.OrderByDescending(c => c.CoinAmount)
.Take(3)
);
然后对其进行GroupJoin
:
var query =
from p in source
join c in postDisableCoinsQuery on p.Id equals c.PostId into postDisableCoins
select new PostProject
{
PostDisableCoins = postDisableCoins.ToList(),
WarStartTime = p.WarStartTime,
WarEndTime = p.WarEndTime,
WarWinner = p.WarWinner,
WarDeclarer = post.WarDeclarer
};
执行时,以上将通过单个 SQL 查询产生所需的结果。
我想在投影中使用 Take() 但我不想生成 N+1 查询,此外在投影中没有 Take() 我面临性能问题。 我将 Take() 与 EF6 一起使用,但我在 EF Core 上遇到了 N+1 问题。
示例投影:
source.Select(post => new PostProject
{
PostDisableCoins = post.PostDisableCoins
.OrderBy(x=>x.CoinAmount)
.Take(3)
.ToList(),
WarStartTime = post.WarStartTime,
WarEndTime = post.WarEndTime,
WarWinner = post.WarWinner,
WarDeclarer = post.WarDeclarer
});
我想要没有 N+1 的 Take(3),有什么建议吗?!?
请注意有关 EF core 2.1 new features:
的文档We have improved our query translation to avoid executing "N + 1" SQL queries in many common scenarios in which the usage of a navigation property in the projection leads to joining data from the root query with data from a correlated subquery. The optimization requires buffering the results from the subquery, and we require that you modify the query to opt-in the new behavior.
例如:
var query = context.Customers.Select(
c => c.Orders.Where(o => o.Amount > 100).Select(o => o.Amount).ToList());
注意包含 .ToList()
的位置。
您需要相应地修改您的投影查询,以启用优化功能。
你的情况可能是:
source.Select(post => new PostProject
{
PostDisableCoins = post.PostDisableCoins
.Select(x => x.OrderBy(x=>x.CoinAmount))
.Select(x => x)
.Take(3)
.ToList(),
WarStartTime = post.WarStartTime,
WarEndTime = post.WarEndTime,
WarWinner = post.WarWinner,
WarDeclarer = post.WarDeclarer
});
这是 EF Core 2.1 实现缺陷。以下是解决方法,但仅当您确实遇到性能问题时才使用它,因为它需要打破导航 属性 连接抽象并使用手动连接,我总是说不应该与 EF (Core) 一起使用。如果用于投影多个集合或作为更复杂查询的一部分,也可能不起作用。
它需要使用横向连接将集合导航 属性 post.PostDisableCoins
的用法替换为 SelectMany
并隐藏 OrderBy
/ Take
运算符(更新使用正确的类型和 PK/FK 个名称):
var postDisableCoinsQuery = source.SelectMany(p =>
db.Set<PostDisableCoin>()
.Where(c => c.PostId == p.Id)
.OrderByDescending(c => c.CoinAmount)
.Take(3)
);
然后对其进行GroupJoin
:
var query =
from p in source
join c in postDisableCoinsQuery on p.Id equals c.PostId into postDisableCoins
select new PostProject
{
PostDisableCoins = postDisableCoins.ToList(),
WarStartTime = p.WarStartTime,
WarEndTime = p.WarEndTime,
WarWinner = p.WarWinner,
WarDeclarer = post.WarDeclarer
};
执行时,以上将通过单个 SQL 查询产生所需的结果。