唉,你忘了告诉我们你的背景。看到你的标签entity-framework 我假设你想以IQueryable 执行LINQ 查询,而不是AsEnumerable。
但是让我们假设您想知道执行时哪些代码会产生最智能的 SQL 代码。
看来您的Player 是一个实体类,与另一个实体存在一对多(或多对多)关系,例如PlayedGames 每个Player 都有零个或多个PlayedGames , 每个PlayedGame 都被一个Player 玩过
在实体框架中,您可以这样设计:
class Player
{
public int Id {get; set;}
public string Name {get; set;}
// every Player has zero or more PlayedGames
public virtual ICollection<PlayedGame> PlayedGames{get; set;}
}
class PlayedGame
{
public int Id {get; set;}
public int Score {get; set;}
// every PlayedGame was played by exactly one Player using foreign key
public int PlayerId{get; set;}
public virtual Player Player {get; set;}
}
假设,不知何故你有一个播放器。您可以通过多种方式获取此 Player 的 PlayedGames。其中两个与您的代码非常相似:
Player player1 = ...
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.Player == player1)
.ToList();
或者你可以去:
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.Player.Id == player1.Id)
.ToList();
您的查询会更详细,但您会明白要点。
还有其他几种对我来说更直观的可能性:
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.PlayerId == player1.Id)
.ToList();
var gamesOfPlayer = player.PlayedGames;
我个人会选择最后一个解决方案。对我来说,这对我来说似乎是最自然的。但是让我们检查一下实体框架将为此编写的 SQL 代码。
计划变更::
当我尝试使用.Where(game => game.Player == player1) 执行查询时出现异常。我已经预料到,毕竟 SQL 不知道你什么时候会认为两个玩家是相同的。我希望实体框架可能足够聪明,可以认为用户的意思是Where the player has an Id equal to player1。
所以我们必须跳过这个。还有三个。
对于测试,我使用了 Microsoft 的 SQL Server Profiler。对 SQL 进行了一些翻译以使其更具可读性
(1) 使用 Player.Id
.Where(game => game.Player.Id == player1.Id)
这会导致以下 SQL:
执行选择
PlayedGames.Id 作为 Id,
PlayedGames.Score 作为得分
来自 dbo.PlayedGames 作为 PlayedGames
其中playedGames.PlayerId = @constant1
请注意,实体框架足够聪明,可以看到 Player.Id 实际上是外键
(2) 使用外键Played.Id
.Where(game => game.PlayerId == player1.Id)
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
来自 dbo.PlayedGames 作为 PlayedGames
其中playedGames.PlayerId = @constant1
因为前面的代码已经足够聪明地使用外键,所以这段代码导致相同的 SQL 也就不足为奇了
(3) 使用 Player 的属性 PlayedGames
var gamesOfPlayer = player.PlayedGames;
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
from dbo.PlayedGames as PlayedGames
where playedGames.PlayerId = @constant1
实体框架未将其转换为来自 Players 和 PLayedGames 的加入!
Entity Framework 检测到我不需要 Player 的任何属性,因此再次使用外键直接转到 PlayedGames 表。
(其实这是我的首选方法,我早就知道会这样)。
结论
- 我不确定你是否能够执行.Where(game => game.Player == player1)。
- 所有其他方法都将使用外键。它们导致相同的 SQL
我个人会选择看起来最自然的代码。对于那些有 SQL 背景的人,这将是在 Where 语句中带有外键的方法。对于那些正在考虑集合的人(在后台将数据库作为抽象事物),它将是Player.PlayedGames的用户。
我还在代码中对此进行了测试,在该代码中,我使用玩家玩过的游戏获取了玩家的一些属性。同样,三个 Where 方法导致相同的 SQL 语句。
使用 Select 而不是 Include
建议:仅在您打算更改获取的值时才使用 Include
查询数据库的速度较慢的部分之一是将获取的数据传输到本地进程。因此,明智的做法是限制传输的数据量。
如果您使用 Include 获取 a player with his PlayedGames,您将获得 PlayedGames 的所有属性,包括外键 PlayerId,或者您已经知道该值等于获取的 Player.Id。因此,如果您获取 1000 个玩家,每个玩家玩 20 场游戏,您将转移 2 万个您已经知道其值的外键。
除此之外,您可能不打算在提取后使用其他属性。
所以而不是:
var playersWithGames = dbContects.Players
.Include(player => player.PlayedGames)
.Where(player => ...)
.ToList();
在大多数用例中,以下方法会更有效:
var playersWitGames = dbContext.Players
.Where(player => ...)
.Select(player => new
{ // select only the properties you plan to use
Id = player.Id,
Name = player.Name,
// not needed for this query: Birthday, emergency telephone number,
// bank account, marital status
Games = player.PlayedGames
.Where(game => ...) // if you don't need all games
.Select(game => new
{
// not sure if needed: game.Id
// certainly not needed: game.PlayerId
Date = game.Date,
Score = game.Score,
...
})
.ToList(),
});
使用获取的结果时,只要您不使用不打算使用的属性,您就不会发现使用 Include 或 Select 有任何区别。
但话又说回来:如果你使用了一个你不打算使用的属性,你会得到一个编译器错误,所以你永远不会转移一个属性意外。
结论:仅当您打算更改获取的项目时才使用 Include
我几乎不感谢您每次计划更改Player with all his PlayedGames,您将更改Player 的属性,或者可能是他的PlayedGames 之一的属性,但很少会一次性更改他的几个PlayedGames .
根据我的经验,我很少在一对多关系中使用Include。有时是一对零或一的关系:“用他的地址更改玩家”,尽管通常是:“更改玩家的地址”