Перевести этот запрос Sql на NHibernate Linq или Критерии? - PullRequest
1 голос
/ 01 февраля 2011

У меня есть схема безопасности, где определенные объекты защищены с помощью ссылки SecureEntity. SecureEntity имеет коллекцию RolePermissions, каждый из которых имеет флаг Allow и приоритет. Идея состоит в том, чтобы сопоставить роли пользователя с RolePermissions в SecureEntity. Например, пользователю может быть разрешено разрешение с наименьшим приоритетом, но ему может быть отказано более высокое, так что это интересующий нас наивысший объект. В этом примере запрашиваемый мной корневой объект называется 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 ». Таким образом, он в основном фильтрует 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 или Criteria. Я потратил несколько часов, пытаясь заставить работать Linq, и потерпел неудачу с различными исключениями «недопустимая операция» во внутреннем соединении с RolePermission. У меня нет большого опыта работы с ICriteria или MultiCriteria, и мне было бы интересно, если кто-нибудь может мне помочь.

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

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

и

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

1 Ответ

0 голосов
/ 04 февраля 2011

Хорошо.Я не мог заставить это работать, используя native 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, иначе вы получите какое-то исключение «неизвестного типа».
...