【问题标题】:LINQ to Entity Framework error with nested many-to-many relationships具有嵌套多对多关系的 LINQ to Entity Framework 错误
【发布时间】:2019-11-14 19:27:06
【问题描述】:

我有以下对象结构(简化了一点......),其中一个用户被赋予一个角色,该角色属于一个或多个组,并且每个组都有一组权限(带有名称)。由于一个权限可以属于多个组,一个组可以包含多个角色,所以结构是这样的:

用户 -> 角色 ->> RoleToGroup -> 组 ->> GroupToPrivilege -> 特权(->> = 列表):

我需要找出是否有任何用户拥有名为“name”的权限。

我尝试了下面的 Linq 语句,我认为它会给我正确的答案:

DbContext.RootObject.Where (x => x.Role.RoleToGroup.Any(y => y.Group.GroupToPrivilege.Any(z=> z.Privilege.Name == "Name")))

这给出了以下 sql:

SELECT 
"Extent1"."ROLE_ID" AS "ROLE_ID", 
FROM "USER" "Extent1"
WHERE ( EXISTS (SELECT 
    1 AS "C1"
    FROM ( SELECT 
    "Extent2"."GROUPS_ID" AS "GROUPS_ID"
    FROM "ROLETOGROUP" "Extent2"
    WHERE ("Extent1"."ROLE_ID" = "Extent2"."ROLE_ID")
)  "Project1"
WHERE ( EXISTS (SELECT 
    1 AS "C1"
    FROM  "GROUPTOPRIVILEGE" "Extent3"
    INNER "PRIVILEGE" "Extent4" ON "Extent3"."PRIVILEGES_ID" = "Extent4"."ID"
    WHERE (("Project1"."GROUPS_ID" = "Extent3"."GROUP_ID") AND ('Name' = "Extent4"."NAME"))
    ))
))

运行时出现以下错误:

ORA-00904:“Extent1”。“ROLE_ID”:标识符无效

如果我删除查询的“Exten1”部分,它运行正常:

SELECT 
"Extent1"."ROLE_ID" AS "ROLE_ID", 
FROM "USER" "Extent1"
WHERE ( EXISTS (SELECT 
1 AS "C1"
FROM ( SELECT 
    "Extent2"."GROUPS_ID" AS "GROUPS_ID"
    FROM "ROLETOGROUP" "Extent2"
    WHERE ("ROLE_ID" = "Extent2"."ROLE_ID")
)  "Project1"
WHERE ( EXISTS (SELECT 
    1 AS "C1"
    FROM  "GROUPTOPRIVILEGE" "Extent3"
    INNER "PRIVILEGE" "Extent4" ON "Extent3"."PRIVILEGES_ID" = "Extent4"."ID"
    WHERE (("Project1"."GROUPS_ID" = "Extent3"."GROUP_ID") AND ('Name' = "Extent4"."NAME"))
    ))
))

但是我怎样才能让 Entity Framework 用 Linq 做同样的事情呢?

【问题讨论】:

    标签: c# oracle entity-framework linq


    【解决方案1】:

    Oracle 不会关联嵌套超过一层的子查询。

    https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1853075500346799932

    这意味着嵌套 2 层深的子查询不能引用顶层表。这是 Oracle 的限制。

    删除查询的"Exten1" 部分会让您产生错觉,认为它的工作原理是"ROLE_ID" 引用同一级别子查询中的列(在本例中为"Extent2"."ROLE_ID"),查询结果将是错误的。

    直接影响生成的 SQL 是相当困难的。

    最简单的解决方案是将现有查询拆分为多个查询。首先从获取权限开始,然后找到匹配的 GroupToPrivilege 条目,依此类推,直到找到用户。

    另一种选择是创建存储过程或视图来检索必要的数据。

    【讨论】:

      【解决方案2】:

      如果您的关系在您的模型上是双向的,您可以尝试颠倒您在关系结构中导航的方式,因此选择首先基于特权。

      它看起来像这样。

      DbContext.Privileges.Where(p => p.Privilege.Name == "Name").SelectMany(p => p.PrivilegeToGroup.Select(pg => pg.Group.GroupToRole.Select(gr => gr.Role.Users)));
      

      附带说明,如果我们使用流畅的 LINQ 语法,这看起来会更友好

      var query = from p in DbContext.Privileges
                  where Privilege.Name == "Name"
                  from pg in p.PrivilegeToGroup
                  from gr in pg.Group.GroupToRole
                  from u in gr.Role.Users
                  select u;
      

      您可能必须更改属性名称以匹配您的模型(我只是做出有根据的猜测)。

      这有望消除对子查询的需求,尽管我无法运行它并查看您的 EF 提供程序将生成的 SQL。试试看,告诉我你得到了什么结果。

      编辑:

      针对您对模型中的关系仅以一种方式进行的评论的回应,您可以手动执行连接。这是一个示例,可让您了解其外观。

      var query = from p in DbContext.Privileges
                  where Privilege.Name == "Name"
                  join gp in DbContext.GroupToPrivilege
                  on p.PrivilegeID equals gp.PrivilegeID
                  join rg in DbContext.RoleToGroups
                  on gp.GroupID equals rg.GroupID
                  join r in DbContext.Role
                  on rg.RoleID equals r.RoleID
                  join u in DbContext.Users
                  on r.RoleID equals u.RoleID
                  select u;
      

      此代码生成的 SQL 将使用连接而不是子查询,这有望绕过@Kaspars 答案中提到的限制。

      【讨论】:

      • 感谢您的回答!不幸的是,在我们的模型中,这些关系只向一个方向发展,因此我无法尝试您提出的解决方案。
      • @EllenLippe 您可以通过手动执行连接来进行手动操作。我会更新我的答案来说明。
      猜你喜欢
      • 1970-01-01
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-22
      相关资源
      最近更新 更多