LINQ to Entity Framework错误与嵌套的多对多关系

时间:2015-09-09 11:02:37

标签: oracle entity-framework linq

我有以下对象结构(简化了一点......),其中给用户一个角色,角色属于一个或多个组,每个组都有一组权限(带有名称)。由于权限可以属于许多组,并且组可以包含许多角色,因此结构如下所示:

  

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

我需要查明是否有任何用户拥有名称为“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"))
    ))
))

但是我如何让实体框架与Linq做同样的事情?

2 个答案:

答案 0 :(得分:4)

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条目,依此类推,直到找到用户为止。

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

答案 1 :(得分:0)

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

它看起来像这样。

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答案中提到的限制。