将此 Sql 查询转换为 NHibernate Linq 或 Criteria?
我有一个安全模式,其中某些实体通过 SecureEntity 引用来保护。 SecureEntity 具有 RolePermissions 的集合,每个 RolePermissions 都有一个允许标志和一个优先级。这个想法是将用户的角色与 SecureEntity 上的 RolePermissions 进行匹配。例如,用户可能被其最低优先级权限允许,但被较高优先级权限拒绝,因此它是我们感兴趣的最高权限。在本例中,我正在查询的根实体称为 ProcessCategory。
(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();
I have a security schema where certain entities are secured by having a SecureEntity reference. A SecureEntity has a collection of RolePermissions, each of which has an Allow flag and a Priority. The idea is to match the user's roles against the RolePermissions on the SecureEntity. For example, a user may be allowed by their lowest priority permission but denied by a higher one, so it is the highest one that we are interested in. In this example the root entity I am querying is called ProcessCategory.
(SecureRoleId is the match for the user's role; SecureRoleName is just a string description.)
Assume a user has roles (1,2) and the SecureEntity has RolePermissions:
SecureRoleId = 1, Priority = 0, Allow = true
SecureRoleId = 2, Priority = 1, Allow = false
In this case the entity would not be selected. But if the user only had role 1, the entity would be selected. Of course, the SecureEntity may contain a bunch of other roles that the user does not have and are irrelevant.
The sql code below works and does this: 'select the entity if the highest priority role permission that the user also has is Allow=true'. So it basically filters RolePermission on the users own roles (IN clause), sorts by Priority, and takes the highest one if that is an Allow.
Here is the 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)
There may be another way to write the above Sql but it does what I need. Ideally I would like to achieve this using NHibernate Linq or Criteria. I spent a few hours trying to get Linq to work and failed with various 'invalid operation' exceptions on the inner join to RolePermission. I don't have much experience with ICriteria or MultiCriteria and would be interested if anybody can help me.
Note that the Fluent mapping for the objects is straightforward:
<some-entity>.References(x => x.SecureEntity)
and
SecureEntity.HasMany(x => x.RolePermissions).Not.Inverse();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的。我无法使用 native NH Linq 使其工作,但这并不意味着它不可能。但我查看了 Linq 的所有 NH 单元测试,但找不到任何等效的东西。
为了让它工作,我创建了一个名为 UserHasPermission 的数据库函数,它可以执行以下操作:
这适用于任何类型的安全实体。然后,我按照本页中的说明将该函数映射为 NH Linq 函数: http://wordpress.primordialcode.com/index.php/2010/10/01/nhibernate-customize-linq-provider-user-defined-sql-函数/。
如果按照这些说明进行操作,则必须在 C# 中创建一个正常的 LinqToObjects 扩展,该扩展具有与数据库扩展相同的签名。然后,您可以执行 NH Linq 查询,如下所示:
我发现的唯一问题是我原来的 Sql 函数返回了一点,我将其映射到 NH 布尔类型。然而,这产生了一个非常奇怪的 sql,其中有几个“Where ''True'' = ''True''”子句在 Sql Server 中崩溃了。所以我将结果更改为整数,一切正常。有点违反直觉,但是...
这样做允许我透明地使用 Linq 进行所有查询,而不影响现有代码,因为它会自动在每个查询前面添加安全检查。
请注意,我查看了 Rhino Security 源代码,它使用了多个标准,这些标准对我来说过于复杂,无法以我有限的 NH 知识来理解。如果我使用 CreateCriteria 完成此操作,我可以将其与 Linq 结合起来吗?
Okay. I couldn't get this to work using native NH Linq, although that doesn't mean that it is not possible. But I looked through all the NH unit tests for Linq and couldn't find anything equivalent.
To get it working I created a database function called UserHasPermission that does everything in:
This works with any kind of secured entity. I then mapped that function as an NH Linq function by following the instructions in this page: http://wordpress.primordialcode.com/index.php/2010/10/01/nhibernate-customize-linq-provider-user-defined-sql-functions/.
If you follow those instructions, you have to create a normal LinqToObjects extension in C# that has an identical signature to your database one. You can then do your NH Linq query like:
The only problem I found was that my original Sql function returned a bit, which I mapped to a NH Boolean type. However this produced a really strange bit of sql that had several "Where ''True'' = ''True''" clauses that blew up in Sql Server. So I changed the result to an integer and everything worked okay. A bit counter-intuitive, but...
Doing it this way allowed me to carry on transparently using Linq for all my queries, without affecting existing code, because it automatically prepended each query with the security check.
Note that I looked in the Rhino Security source code and it uses a multiple criteria that is much too complex for me to understand with my limited NH knowledge. If I had done it using CreateCriteria, could I have combined it with Linq though?