Сценарий
Я построил структуру базы данных, представляющую дерево категорий, чтобы помочь классифицировать некоторые данные, которые мы сохранили. Реализация заключается в том, что каждая запись в таблице Category имеет обнуляемый внешний ключ обратно в таблицу Category для представления родительской Category этой категории (один ко многим), что по существу позволяет использовать подкатегории в пределах более широкого родительского уровня. Существует таблица CategoryMembership, которая связывает запись в таблице Item с соответствующей таблицей Category (многие ко многим). Я создал DBML для этой базы данных, и у нее есть структура доступа к членам, которая включает следующее:
Dim aCategory As New Category()
Dim aParentCategory As Category = aCategory.Parent
Dim aChildCategoryCollection As EntitySet(Of Category) = aCategory.Subcategories
Dim aMembershipCollection As EntitySet(Of CategoryMembership) = aCategory.CategoryMemberships
Каждый элемент в aMembershipCollection имеет следующую структуру доступа к элементам:
Dim aMembership As CategoryMembership = aMembershipCollection.First()
Dim aLinkedCategory As Category = aMembership.Category
Dim aLinkedItem As Item = aMembership.Item
Требование
Я пытаюсь создать выражение LINQ, которое позволило бы мне определить, какие Items имеют CategoryMemberships для запрошенного Category (т.е. aCategory.id = myID) или членства для потомков запрошенного Category, идея заключается в том, что я хочу, чтобы все Items были в родительском категория или ее несколько уровней подкатегорий.
По сути, запрос будет построен примерно так:
Dim results As IQueryable(Of Item) = _
From cm In db.CategoryMemberships.Where(myInCategoryPredicate(myID)) _
Select cm.Item
...где myInCategoryPredicate возвращает объект выражения LINQ, который поможет мне сделать это определение. Это, конечно, работает из предположения, что таблица CategoryMembership — это место, с которого нужно начать извлекать IQueryable(Of Item). Возможно, я сделал ошибочное предположение, и именно поэтому я пришел за советом.
Проблема
Мне трудно видеть лес за деревьями. Я не могу определить, следует ли мне начать создание предиката с Category или с CategoryMembership, а также я не могу понять необходимый код, который мог бы выполнить то, что я хотел бы. Я надеюсь, что кто-нибудь еще, уже построивший аналогичную древовидную структуру для базы данных, сможет помочь мне ориентироваться в классах DBML.
Доступные ресурсы
Ранее я использовал PredicateBuilder и относительно хорошо знаком с его работой, но я не смогли разработать способ перемещения вверх по дереву и рекурсивного построения предиката, который указывал бы, находится ли Статья в категории, которая является либо запрошенной категорией, либо ее дочерней. До сих пор я создал следующее с очень заметным пробелом, помеченным SomeRecursiveCall():
Private Function InCategory(ByVal myID As Integer) As Expression(Of Func(Of CategoryMembership, Boolean))
Dim predicate = PredicateBuilder.False(Of CategoryMembership)()
predicate = predicate.Or(Function(cm) cm.fkCategoryID = myID OrElse SomeRecursiveCall())
Return predicate
End Function
Однако я понимаю, что построитель предикатов здесь может быть бесполезен и может потребоваться другое направление.
Я полагал, что всегда есть возможность выбрать запись Category для запрошенного идентификатора и рекурсивно построить список идентификаторов из нее и всех членов Subcategories, а затем использовать этот список для оценки сравнения .Contains() в этом списке, но я задавался вопросом, не было ли других вариантов, которые не казались такими уродливыми.