【问题标题】:Create a left join query without navigation property创建没有导航属性的左连接查询
【发布时间】:2020-04-10 09:50:00
【问题描述】:

我会使用 EFCore 方法语法来实现这个目标

  • 给定用户检索所有角色,如果给定用户具有该角色,则设置 IsInRole = 1,否则为 0

在 tsql 中类似于

CREATE TABLE USER(USERNAME VARCHAR(200) PRIMARY KEY);
CREATE TABLE ROLE(ROLENAME VARCHAR(200) PRIMARY KEY);
CREATE TABLE USERROLE(USERNAME VARCHAR(200), ROLENAME VARCHAR(200));

INSERT INTO USER VALUES('LELU');
INSERT INTO ROLE VALUES('ROLE1');
INSERT INTO ROLE VALUES('ROLE2');
INSERT INTO USERROLE VALUES('LELU', 'ROLE1');

SELECT R.ROLENAME, CASE WHEN T.ROLEID IS NULL THEN 0 ELSE 1 END ISINROLE
FROM ROLE R
LEFT JOIN (
  SELECT UR.ROLENAME ROLEID
  FROM USERROLE UR
  WHERE UR.USERNAME='LELU'
) T ON T.ROLEID=R.ROLENAME 

ROLENAME                ISINROLE
-------------------------------------
ROLE1                   1
ROLE2                   0

我想使用没有导航属性的 EF 核心来实现这个目标。

我有以下实体

public class User 
{
    [Key]
    public string Username {get; set;}
}

public class Role 
{
    [Key]
    public string Rolename {get; set;}
}

public class UserRole 
{
    public string Username {get; set;}
    public string Rolename {get; set;}
}

where UserRole (Username, Rolename) are primary keys

public class MyDbContext : DbContext 
{
    public DbSet<User> Users {get; set;}
    public DbSet<Role> Roles {get; set;}
    public DbSet<UserRole> UsersRoles {get; set;}
    ...
}

我在网上做了一些研究,但没有找到任何可以在没有导航属性的情况下完成左连接的方法。

不使用导航属性既不在 EFCore 中注入 原始查询,我如何才能实现这个目标?

【问题讨论】:

  • 对不起,你为什么不使用导航属性呢?映射关系以允许 EF 构建查询是使用实体框架的重点。问“如果没有导航属性或注入 SQL,我怎么能做到这一点”就像问“我怎么能在不移动腿的情况下跑马拉松?”工具是有用的,有什么理由避免使用?
  • 我同意你的观点,但在这种特殊情况下,我无法修改“用户”和“角色”类。因此,我试图在没有导航属性的情况下实现这个目标。此外,我正在学习实体框架,所以我想知道是否可以在没有导航属性的情况下实现左连接
  • 如果您不想/不能使用导航属性,那么与EF(Core)没有任何共同之处。您可以在通用 LINQ 中搜索左外连接语法 - 有许多“官方”模式的示例(包括查询和方法语法)。顺便说一句,连接的方法语法(你称之为“lambda 查询”)是一个真正的痛苦并且非常难以理解,所以如果你不能使用导航属性,至少使用查询语法 - 它会更接近 SQL 查询。

标签: entity-framework entity-framework-core


【解决方案1】:

没有导航属性也是可能的。无论如何它都不漂亮,并且取决于您希望从关联实体中获得的详细信息,它会变得更加丑陋。但是,要获取带有标志的角色列表,以表明所提供的用户是否已分配给他们:

using (var context = new MyDbContext())
{
    var roles = context.Roles
        .SelectMany(r => context.UserRoles.Where(ur => ur.RoleName == r.RoleName 
             && ur.UserName == userName).DefaultIfEmpty(),
             (r, ur) => new
             {
                 RoleName = r.RoleName,
                 IsInRole = ur != null
             }).ToList();
}

这将返回每个角色,以及提供的userName 值是否通过DefaultIfEmpty() 使用左联接分配给该角色。

【讨论】:

  • 谢谢史蒂夫·派。我已经尝试过您的解决方案并且它有效。正如您在此处看到的stackoverflow.com/a/54715309/11084254 ASP.NET Identity 不使用 IdentityUser / IdentityRole / UsersRoles 实体内的导航属性。因此,我用我必须使用的表格的简化示例编写了这个问题。无论如何,谢谢你!此解决方案有效!
【解决方案2】:

虽然 LINQ 查询语法通常不受欢迎,但我相信它非常适合加入没有导航属性的实体的场景。

var query = 
    from role in context.Roles
    join userRole in context.UserRoles on userRole.RoleName equals role.RoleName
    select new 
    {
        ...
    };

虽然左连接不是很好。

var query = 
    from role in context.Roles
    join userRole in context.UserRoles on userRole.RoleName equals role.RoleName into ur
    from userRole in ur.DefaultIfEmpty()
    select new 
    {
        RoleName = role.RoleName,
        IsInRole = userRole != null
    };  

作为奖励,如果您需要执行多个连接,使用 LINQ 查询语法,您只需添加一个额外的行来描述连接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    • 1970-01-01
    相关资源
    最近更新 更多