EF上下文中基于动态角色的身份验证过滤器

时间:2017-06-09 12:23:07

标签: c# sql-server entity-framework linq-to-entities

我一直在努力弄清楚github

上究竟发生了什么

所有这一切的关键点是我试图将这样的过滤器应用于上下文......

builder.Filter("UserProcesses",
    (Process p, string user) => !p.Roles.Any() || p.Roles.Any(r => r.Users.Any(u => u.UserName == user && (bool)r.Read)),
    (CoreDataContext ctx) => ctx.AuthInfo.Name
);

这会在评估表达式树并尝试生成SQL查询时在EF中生成运行时表达式,因此我想我可以尝试其他方法并解决它并提出这个......

builder.Filter("UserProcesses",
    (Process p, string user) => !p.Roles.Any() || p.Roles.Any(r => r.Read ?? false && r.Users.Any(u => u.UserName == user)),
    (CoreDataContext ctx) => ctx.AuthInfo?.Name
);

这样做的最终结果......

var proesses = ctx.Processes.ToList();

...就是我得到EF生成的这个SQL查询(如在profiler中看到的那样)......

exec sp_executesql N'SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[FirstProcessStepId] AS [FirstProcessStepId], 
    [Extent1].[Name] AS [Name]
    FROM [Workflow].[Processes] AS [Extent1]
    WHERE ( NOT EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[ProcessRoles] AS [Extent2]
        WHERE [Extent1].[Id] = [Extent2].[Process_Id]
    )) OR ( EXISTS (SELECT 
        1 AS [C1]
        FROM  [dbo].[ProcessRoles] AS [Extent3]
        INNER JOIN [dbo].[Roles] AS [Extent4] ON [Extent4].[Id] = [Extent3].[Role_Id]
        WHERE ([Extent1].[Id] = [Extent3].[Process_Id]) AND ((CASE WHEN ([Extent4].[Read] IS NULL) THEN cast(0 as bit) ELSE [Extent4].[Read] END) = 1)
    )) ',N'@DynamicFilterParam_000002 bit',@DynamicFilterParam_000002=NULL

最终结果是我的测试数据应该有1个过程实体被过滤掉,因为它链接到用户不在的角色。

1 个答案:

答案 0 :(得分:0)

所以我做了一些挖掘,事实证明我们可以像这样构建过滤器......

builder.Filter("UserProcesses",
    (Process p, string user) => !p.Roles.Any() || p.Roles.Any(r => r.Read == true && r.Users.Any(u => u.UserName == user)),
    (CoreDataContext ctx) => ctx.AuthInfo?.Name
);

这里的最终结果是角色属性在角色表达式范围内而不是在嵌套表达式中进行评估。

然后我绊倒了一个类似说法的场景......一个日历有很多事件,我想要一个规则,为每个人说这样的事情......

  

当用户处于授予他们的角色时,用户可以查看所有日历   读取权限。用户可以查看日历中的所有事件   他们可以访问的日历。

这导致生成复杂SQL的问题,所以我最终写出了这样的规则......

builder.Filter("UserCalendars",
    (Calendar c, string user) => !c.Roles.Any() || c.Roles.Any(r => r.Read == true && r.Users.Any(u => u.UserName == user)),
    (CoreDataContext ctx) => ctx.AuthInfo?.Name
);

builder.Filter("UserEvents",
    (Event e, IQueryable<int> calIds) => calIds.Contains(e.CalendarId),
    (CoreDataContext ctx) => ctx.Calendars.Select(c => c.Id)
);

这允许框架从单个Db.Events.ToList()调用中找出它实际上应该运行查询以首先获取所有日历的id,然后将其作为参数传递给实际我问的事件问题。

那有多酷!!