强制内连接与多对多关系实体框架

rya*_*lit 5 c# linq linq-to-entities linq-query-syntax entity-framework-5

我的数据库中有一个多对多关系设置,如下所示:

User
-------
Id (PK, Identity)
First
Last
...various other fields

Skill
-------
Id (PK, Identity)
Description

UserSkill
-----------
UserId (PK, FK on User.Id)
SkillId (PK, FK On Skill.Id)
Run Code Online (Sandbox Code Playgroud)

当我在 DbContext 上运行此 LINQ 查询时:

from u in Users 
from s in u.Skills 
where s.Id == 5 
select new 
{
    u.Id,
    s.Description
})
Run Code Online (Sandbox Code Playgroud)

生成的 SQL 包含所有内部联接,这就是我想要的:

SELECT 
[Extent1].[UserId] AS [UserId], 
[Extent2].[Description] AS [Description]
FROM  [dbo].[UserSkill] AS [Extent1]
INNER JOIN [dbo].[Skill] AS [Extent2] ON [Extent1].[SkillId] = [Extent2].[Id]
WHERE 5 = [Extent2].[Id]
Run Code Online (Sandbox Code Playgroud)

但是,当我添加一个简单的额外 where 子句时:

from u in Users 
from s in u.Skills 
where s.Id == 5 
    && u.Last == "test"
select new 
{
    u.Id,
    s.Description
})
Run Code Online (Sandbox Code Playgroud)

现在生成的 SQL 使用子查询:

[Extent1].[Id] AS [Id], 
[Filter1].[Description] AS [Description]
FROM  [dbo].[User] AS [Extent1]
INNER JOIN  (SELECT [Extent2].[UserId] AS [UserId], [Extent3].[Description] AS [Description]
    FROM  [dbo].[UserSkill] AS [Extent2]
    INNER JOIN [dbo].[Skill] AS [Extent3] ON [Extent3].[Id] = [Extent2].[SkillId]
    WHERE 5 = [Extent3].[Id] ) AS [Filter1] ON [Extent1].[Id] = [Filter1].[UserId]
WHERE 'test' = [Extent1].[Last]
Run Code Online (Sandbox Code Playgroud)

也许我遗漏了一些东西,但我认为 EF 只会为此查询将另一个联接添加回 User 表,并且能够在 User.Last 上执行 where 而不是执行子查询。有什么办法可以强制这种行为吗?难道我做错了什么?

谢谢。


更新

Cosmin,我希望查询结果如下:

SELECT u.Id, s.Description
FROM [User] u INNER JOIN
        [UserSkill] us ON u.Id = us.UserId INNER JOIN
        [Skill] s ON us.SkillId = s.Id
WHERE s.Id = 2 AND u.Last = 'test'
Run Code Online (Sandbox Code Playgroud)

Jos*_*osh 3

看起来这是EF目前没有做的优化。就我个人而言,我会坚持使用它生成的子查询,除非性能成为问题。

但是,如果您愿意失去用户和技能的直接导航属性,您可以对中间表进行建模以获取您正在查找的查询。

public class User
{
    public int Id { get; set; }
    public string First { get; set; }
    public string Last { get; set; }

    public virtual ICollection<UserSkill> UserSkills { get; set; }
}

public class UserSkill
{
    public int Id { get; set; }
    [Required]
    public User User { get; set; }
    [Required]
    public Skill Skill { get; set; }

}

public class Skill
{
    public int Id { get; set; }
    public string Description { get; set; }

    public virtual ICollection<UserSkill> UserSkills { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

那么下面的查询将产生一个连接而不是子查询

 from x in db.UserSkills 
 where x.Skill.Id == 5 && x.User.Last == "test"
 select new {x.User.Id, x.Skill.Description};
Run Code Online (Sandbox Code Playgroud)