Я изо всех сил пытаюсь придумать правильные слова, чтобы обобщить эту проблему, поэтому любой вклад в то, что я могу добавить, чтобы прояснить это, будет оценен.
Основной сценарий таков: у меня есть базовая CMS (со страницами, пользователями и т.д.). Страница - это объект данных LINQ, который непосредственно сопоставляется с таблицей страниц.
Я добавил метод к классу страниц, называемому GetUserPermissions. Этот метод принимает UserId и возвращает класс non-LINQ под названием PagePermissionSet, который описывает, что пользователь может сделать. PagePermissionSet вычисляется по запросу LINQ.
Теперь я хочу получить список страниц, к которым пользователь имеет доступ. Идеальная реализация будет следующей:
from page in mDataContext.Pages
where page.GetUserPermissions(userId).CanView
select page
Это не удается, заявив, что для GetUserPermissions (что достаточно разумно) не существует эквивалента SQL, или после некоторого рефакторинга метода, что элемент CanView не может быть вызван в IQueryable.
Попытка состоит в том, чтобы добавить метод DataContext, который возвращает все разрешения для каждой страницы/пользователя как IQueryable:
IQueryable<PagePermissionSet> GetAllPagePermissions()
Затем я попытался присоединиться к этому набору результатов:
IQueryable<Page> GetAllPages(Guid? userId) {
var permissions = mDataContext.GetAllPagePermissions();
var pages =
from page in mDataContext.WikiPages
join permission in permissions on Page.FileName equals permission.PageName
where permission.CanView && permission.UserId == userId
select page;
return pages;
}
Это приводит к ошибке: "Член WikiTome.Library.Model.PagePermissionSet.PageName" не поддерживает перевод в SQL. "
PagePermissionSet - это всего лишь оболочка, содержащая данные из предложения select в GetUserPermissions и инициализируется следующим образом:
select new PagePermissionSet(pageName, userName, canView, canEdit, canRename)
Из-за всего этого... Как я могу повторно использовать запрос LINQ в Page.GetUserPermissions в другом запросе? Я определенно не хочу дублировать код, и я бы предпочел не переводить его в SQL для включения в качестве представления на данный момент.
Сегодня я смог решить основную часть этой проблемы.
Ошибка "член" WikiTome.Library.Model.PagePermissionSet.PageName 'не поддерживает перевод на SQL. " был вызван тем, как я инициализировал объекты PagePermissionSet.
Я инициализировал их с помощью конструктора, например:
select new PagePermissionSet(pageName, userName, canView, canEdit, canRename)
Однако, чтобы LINQ правильно отслеживал свойства, его необходимо инициализировать следующим образом:
select new PagePermissionSet { PageName=pageName, UserName = userName, CanView = canView, CanEdit = canEdit, CanRename = canRename }
С этим изменением я могу создать метод, который возвращает IQueryable<PagePermissionSet>
, а затем присоединяется к моему запросу к этому (как во втором примере).
У вас есть несколько вариантов.
1) Быстрое и грязное решение заключается в использовании AsEnumerable()
с вашим запросом, чтобы привести всю таблицу Pages
к стороне клиента, а затем работать с ней. Для небольших таблиц это должно быть хорошо, однако для больших таблиц оно будет неэффективным и приведет к проблемам с производительностью в зависимости от размера. Если вы решите использовать это, помните о том, как он действует. Это означает, что вы обновляете свой код до:
from page in mDataContext.Pages.AsEnumerable()
where page.GetUserPermissions(userId).CanView
select page
2) Более привлекательным решением было бы создание хранимой процедуры или UDF на SQL-сервере, который вы вызывали бы из DataContext и передавали параметры. Взгляните на пост Скотта Гу: LINQ to SQL (часть 6 - Извлечение данных с помощью хранимых процедур).
Затем вы можете написать что-то вроде:
mDataContext.GetUserPermissions(userId)
Вся логика, которую вы выполняете в своем коде, будет написана в SQL, и вы вернете страницы просмотра для данного пользователя. Это обходит использование свойств PagePermissionSet
, которые не поддерживают перевод SQL.
Возможно, вам нужен скомпилированный запрос?