【问题标题】:Querying for any entries after traversing multiple conditional includes in EF Core 5在遍历 EF Core 5 中的多个条件包含后查询任何条目
【发布时间】:2021-11-27 14:48:15
【问题描述】:

考虑以下数据模型:

  • 一个委托人有多个角色(多对多);
  • 角色授予多个权限(多对多);

现在我想使用 LINQ 来确定 Principal 是否具有权限,即他是否在任何拥有此权限的角色中。 对于这种情况,通常我会在连接表上使用AnyAsync,但由于我要遍历多个连接表,所以我真的很挣扎。我最初想出了这个:

    var query = context.Principals
        .Where(p => p.Id == "SomeUUID")
        .Include(p => p.Roles)
        .ThenInclude(r => r.Permissions
        .Where(p => p.Name == "SomePermission"));

但现在我必须再次通过 Principal 遍历(可能在内存中)附加的角色以搜索 Any 权限。

有没有办法可以在同一个查询中顺利应用此检查?

编辑:这是生成的 SQL 以补充 Parks 的(底部)答案:

SELECT CASE
      WHEN EXISTS (
          SELECT 1
          FROM [Principals] AS [a]
          INNER JOIN (
              SELECT [a1].[Id], [a1].[Description], [a1].[DisplayName], [a0].[RoleId], [a0].[PrincipalId]
              FROM [PrincipalRoles] AS [a0]
              INNER JOIN [Roles] AS [a1] ON [a0].[RoleId] = [a1].[Id]
          ) AS [t] ON [a].[PrincipalId] = [t].[PrincipalId]
          INNER JOIN (
              SELECT [a3].[Id], [a3].[Name], [a2].[RoleId], [a2].[PermissionId]
              FROM [RolePermissions] AS [a2]
              INNER JOIN [Permissions] AS [a3] ON [a2].[PermissionId] = [a3].[Id]
          ) AS [t0] ON [t].[Id] = [t0].[RoleId]
          WHERE ([a].[PrincipalId] = "SomePrincipal") AND ([t0].[Name] = "SomePermission")) THEN CAST(1 AS bit)
      ELSE CAST(0 AS bit)
END

SELECT CASE
      WHEN EXISTS (
          SELECT 1
          FROM [Principals] AS [a]
          WHERE ([a].[PrincipalId] = "SomePrincipal") AND EXISTS (
              SELECT 1
              FROM [PrincipalRoles] AS [a0]
              INNER JOIN [Roles] AS [a1] ON [a0].[RoleId] = [a1].[Id]
              WHERE ([a].[PrincipalId] = [a0].[PrincipalId]) AND EXISTS (
                  SELECT 1
                  FROM [RolePermissions] AS [a2]
                  INNER JOIN [Permissions] AS [a3] ON [a2].[PermissionId] = [a3].[Id]
                  WHERE ([a1].[Id] = [a2].[RoleId]) AND ([a3].[Name] = "SomePermission")))) THEN CAST(1 AS bit)
      ELSE CAST(0 AS bit)
END

【问题讨论】:

    标签: entity-framework linq .net-core entity-framework-core


    【解决方案1】:

    要查看主体是否在 Roles 中具有在 Permissions 中具有 Permission 的 Role,您可以使用以下 Where 条件:

    var result = await context.Principals
        .Where(p => p.Id == "SomeUUID" && p.Roles.Any(r => r.Permissions.Any(x => x.Name == "SomePermission")))
        .FirstOrDefaultAsync();
    

    如果 result 为 null,则 Principal 不存在或没有您检查的权限。如果result 仍然需要导航属性,那么您可以添加最初包含在您的问题中的包含语句。

    【讨论】:

    • 谢谢,我只是去了AnyAsync,因为我根本不需要检索结果。我最初的查询正是我期望的结果看起来相似。
    【解决方案2】:

    您可能正在寻找此查询:

    var hasPermssion = context.Principals
        .Where(p => p.Id == "SomeUUID")
        .SelectMany(p => p.Roles)
        .SelectMany(r => r.Permissions)
        .Any(p => p.Name == "SomePermission");
    

    生成的SQL语句如下:

    SELECT CASE
          WHEN EXISTS (
              SELECT 1
              FROM [Principals] AS [a]
              INNER JOIN (
                  SELECT [a1].[Id], [a1].[Description], [a1].[DisplayName], [a0].[RoleId], [a0].[PrincipalId]
                  FROM [PrincipalRoles] AS [a0]
                  INNER JOIN [Roles] AS [a1] ON [a0].[RoleId] = [a1].[Id]
              ) AS [t] ON [a].[PrincipalId] = [t].[PrincipalId]
              INNER JOIN (
                  SELECT [a3].[Id], [a3].[Name], [a2].[RoleId], [a2].[PermissionId]
                  FROM [RolePermissions] AS [a2]
                  INNER JOIN [Permissions] AS [a3] ON [a2].[PermissionId] = [a3].[Id]
              ) AS [t0] ON [t].[Id] = [t0].[RoleId]
              WHERE ([a].[PrincipalId] = "SomePrincipal") AND ([t0].[Name] = "SomePermission")) THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
    END
    

    【讨论】:

    • 我认为您可以在此查询中使用 Where 而不是 Any。
    • 不确定,我们必须检查 Principal 是否有权限,并且检索记录是多余的。
    • 是的,但它只是检索匹配的权限。要么应该工作。无论如何,我认为 SelectMany 或它的 LINQ 等价于 stacking FROM 是更简单的模式。
    • Any 更改为FirstOrDefault 很容易。在这里受益,SQL Server 会很高兴:只有连接和一个 EXISTS。
    • 是的,AnyAsync 就足够了。我确实相信这是更简洁的解决方案,但我注意到它的性能比@Jeffrey Parks 解决方案略差,至少在可用数据很少的情况下。这可能是由于查询角色/权限时内部连接的SELECT 包括所有列吗?有没有一种方法可以在这里应用投影来从SelectMany 中挤出更多的性能?
    猜你喜欢
    • 2021-07-12
    • 2017-02-18
    • 1970-01-01
    • 2020-03-19
    • 1970-01-01
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    相关资源
    最近更新 更多