Перевести этот запрос Sql в NHibernate Linq или критерии?

У меня есть схема безопасности, в которой определенные объекты защищены ссылкой на SecureEntity. SecureEntity имеет набор RolePermissions, каждый из которых имеет флаг разрешения и приоритет. Идея состоит в том, чтобы сопоставить роли пользователя с RolePermissions на SecureEntity. Например, пользователю может быть разрешено разрешение с самым низким приоритетом, но ему может быть отказано с помощью разрешения с более высоким приоритетом, поэтому оно является наивысшим, что нас интересует. В этом примере корневой объект, который я запрашиваю, называется ProcessCategory.

Схема

(SecureRoleId соответствует роли пользователя; SecureRoleName - это просто строковое описание.)

Предположим, что у пользователя есть роли (1,2), а SecureEntity имеет RolePermissions:

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

В этом случае объект не будет выбран. Но если бы у пользователя была только роль 1, объект был бы выбран. Конечно, SecureEntity может содержать множество других ролей, которых у пользователя нет и которые не имеют отношения к делу.

Приведенный ниже код sql работает и делает следующее: «выберите объект, если разрешение роли с наивысшим приоритетом, которое также имеет пользователь, - Allow = true». Таким образом, он в основном фильтрует RolePermission для собственных ролей пользователей (предложение IN), сортирует по приоритету и берет наивысший из них, если это разрешение.

Вот 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 или критериев. Я потратил несколько часов, пытаясь заставить Linq работать, и потерпел неудачу с различными исключениями «недопустимая операция» при внутреннем присоединении к RolePermission. У меня нет большого опыта работы с ICriteria или MultiCriteria, и мне было бы интересно, может ли кто-нибудь мне помочь.

Обратите внимание, что сопоставление объектов Fluent простое:

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

и

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

person Rob Kent    schedule 01.02.2011    source источник


Ответы (1)


Хорошо. Мне не удалось заставить это работать с использованием собственного NH Linq, хотя это не значит, что это невозможно. Но я просмотрел все модульные тесты NH для Linq и не нашел ничего эквивалентного.

Чтобы заставить его работать, я создал функцию базы данных под названием 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/.

Если вы будете следовать этим инструкциям, вам нужно будет создать обычное расширение LinqToObjects на C #, которое будет иметь такую ​​же подпись, что и подпись вашей базы данных. Затем вы можете выполнить свой запрос 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 (или коде), иначе вы получите какой-то исключение "неизвестного типа".
person Rob Kent    schedule 04.02.2011