将此Sql查询转换为NHibernate Linq或Criteria?

时间:2011-02-01 18:29:43

标签: nhibernate linq-to-nhibernate

我有一个安全架构,通过拥有SecureEntity引用来保护某些实体。 SecureEntity有一组RolePermissions,每个RolePermissions都有一个Allow标志和一个Priority。我们的想法是将用户的角色与SecureEntity上的RolePermissions相匹配。例如,用户可能被其最低优先级权限允许但被较高权限拒绝,因此它是我们感兴趣的最高权限。在此示例中,我查询的根实体称为ProcessCategory。

Schema

(SecureRoleId是用户角色的匹配; SecureRoleName只是一个字符串描述。)

假设用户具有角色(1,2)并且SecureEntity具有RolePermissions:

SecureRoleId = 1, Priority = 0, Allow = true
SecureRoleId = 2, Priority = 1, Allow = false

在这种情况下,不会选择实体。但如果用户只有角色1,则会选择该实体。当然,SecureEntity可能包含一堆用户没有的其他角色,并且无关紧要。

下面的sql代码可以工作并执行此操作:'如果用户也拥有的最高优先级角色权限是Allow = true,则选择实体'。因此,它基本上过滤了用户自己角色(IN子句)上的RolePermission,按优先级排序,如果是允许,则取最高值。

这是Sql:

select pc.* from ProcessCategory pc
join SecureEntity se 
    join RolePermission rp on se.SecureEntityId = rp.SecureEntityId 
on pc.SecureEntityId = se.SecureEntityId
where rp.RolePermissionId = (select top 1 RolePermissionId 
                from RolePermission
                where Allow = 1
                and SecureEntityId = se.SecureEntityId
                and SecureRoleId in(0,1)
                order by Priority desc)

可能有另一种方法来编写上面的Sql,但它可以满足我的需要。理想情况下,我想使用NHibernate Linq或Criteria实现这一目标。我花了几个小时试图让Linq工作,并且在内部联接到RolePermission时出现了各种“无效操作”异常。我对ICriteria或MultiCriteria没有太多经验,如果有人可以帮助我,我会感兴趣。

请注意,对象的Fluent映射非常简单:

 <some-entity>.References(x => x.SecureEntity) 

 SecureEntity.HasMany(x => x.RolePermissions).Not.Inverse();

1 个答案:

答案 0 :(得分:0)

好。我无法使用原生 NH Linq来实现这一点,尽管这并不意味着它不可能。但我查看了Linq的所有NH单元测试,但找不到任何相同的东西。

为了使它工作,我创建了一个名为UserHasPermission的数据库函数,它可以执行以下任何操作:

on pc.SecureEntityId = se.SecureEntityId
where rp.RolePermissionId = (select top 1 RolePermissionId 
            from RolePermission
            where Allow = 1
            and SecureEntityId = se.SecureEntityId
            and SecureRoleId in(0,1)
            order by Priority desc)

这适用于任何类型的安全实体。然后按照本页中的说明将该函数映射为NH Linq函数:http://wordpress.primordialcode.com/index.php/2010/10/01/nhibernate-customize-linq-provider-user-defined-sql-functions/

如果您按照这些说明操作,则必须在C#中创建一个普通的LinqToObjects扩展,该扩展与您的数据库签名具有相同的签名。然后,您可以执行NH Linq查询,如:

return base.Query<T>().Where(c => ((ISecureEntity)c)
            .SecureEntity.Id
            .UserHasPermissions(user.SecureRoleIdsCsv) == 1);

我发现的唯一问题是我的原始Sql函数返回了一点,我将其映射到NH布尔类型。然而,这产生了一个非常奇怪的sql,其中有几个“Where''True''=''True''”子句在Sql Server中爆炸了。所以我将结果更改为整数,一切正常。有点违反直觉,但......

这样做可以让我在不影响现有代码的情况下透明地使用Linq进行所有查询,因为它会自动为每个查询添加安全检查。

请注意,我查看了Rhino Security源代码,它使用了一个过于复杂的多重标准,我无法理解我的NH知识。如果我使用CreateCriteria完成它,我可以将它与Linq结合使用吗?

  • 上面链接中的说明并不清楚当您创建自己的方言来注册您的Sql函数时,您必须确保在NH配置文件(或代码)中引用它,否则您将获得某种“未知类型”例外。