Я новичок в EF. Помогите пожалуйста найти решение для работы с иерархиями с нефиксированной глубиной в EF. Имеется иерархическая таблица:
CREATE TABLE [dbo].[Department](
[DepartmentID] [int] IDENTITY(1,1) NOT NULL,
[OrganizationID] [int] NULL,
[Name] [nvarchar](50),
[ShortName] [nvarchar](50) NULL,
[ParentID] [int] NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Department] WITH CHECK ADD CONSTRAINT [FK_Department_Department] FOREIGN KEY([ParentID])
REFERENCES [dbo].[Department] ([DepartmentID])
ALTER TABLE [dbo].[Department] WITH CHECK ADD CONSTRAINT [FK_Department_Organization] FOREIGN KEY([OrganizationID])
REFERENCES [dbo].[Organization] ([OrganizationID])
При вызове метода
using (someEntities app = new someEntities())
{
List<Department> departments = someEntities.Departments.ToList();
}
получаю List<Department> с приблизительно такой древовидной структурой:
+ Department 1 (First level) parent == null
++ Department 4 (Second level) parent == Department 1
+++ Department 6 (Third level) parent == Department 4 + Department 4 (Second level) parent == Department 1 ++ Department 6 (Third level) parent == Department 4
+ Department 2 (First level) parent == null
++ Department 5 (Second level) parent == Department 2
+ Department 3 (First level) parent == null + Department 5 (Second level) parent == Department 2 + Department 6 (Third level) parent == Department 4
Полужирным курсивом выделены избыточные данные, дублирующиеся как на самом верхнем уровне, так и в качестве детей соответсвующих родителей. Ясно, что в цикле можно удалить из списка все элементы, находящиеся на верхнем уровне, у которых parent != null вместе со всеми их детьми. Также ясно, как можно получить правильное дерево, если известна глубина иерархии, напр.,
using (someEntities app = new someEntities())
{
List<Department> departments = (someEntities.Departments
.Include("Children.Children")
.Where(e => e.Parent == null)).ToList();
}
Вопрос заключается в том, как получить сразу правильное дерево для иерархии с нефиксированной (заранее неизвестной) глубиной?
Re: [EF]. Иерархии с нефиксированной глубиной в Entity Frame
Если не секрет — вам надо тупо загрузить всё содержимое таблицы или только все подузлы одного узла (независимо от уровня вложенности)?
Если всё дерево — то списком или только корни?
Изначально проблема в том, что параллельно селекту со всей таблицы оно строит граф объектов — отсюда ваша структура. Как такового дублирования здесь нет — просто ссылки на одно и то же подразделение лежат в списке и в свежепостроенном графе. Так что ничего страшного.
Т.е. если хотите получить прекэшированное дерево — сначала забираете весь список, делаете его ToList() /*чтобы кэшировать*/ и делаете по этому списку where простым linq to objects. Вуаля
Ещё варианты:
1 — выгребать только корни + инклюдить с запасом (тысячи хватит? ). // кажется будет аццкий sql
2 — попробовать написать хранимку и выгребать данные через неё... // Весь гемморой прекрасно представляю
3 — Попытаться поискать на тему recursive LINQ queries. // куда ж без RTFM?
Если не трудно, попрофайлите SQL, что приходит на сервер при использовании .Include и киньте сюда. Может чего и наклюнется...
Да, я бы осторожней подходил к EF. В нынешнем состоянии оно не очень-то подходит для типичных десктопных приложений. Ссылка на общение с товарищем из EF team: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/172ae667-d20a-4dbf-9e98-39b90387b4f4
Читайте — узнаете много нового. Он там рекомендует писать model view поверх EF и использовать его для биндинга etc. Т.е. биндинга из коробки и без проблем у вас не будет. Не пугает — вперёд
P.S. Боюсь что без костылей не обойтись. EF — он такой загадочный...
P.P.S. А у вас подразделения в 1 иерархии могут относиться к нескольким организациям? Во люди живут...
Re[2]: [EF]. Иерархии с нефиксированной глубиной в Entity Fr
Большое спасибо за ответ, Sinix.
S>Если не секрет — вам надо тупо загрузить всё содержимое таблицы или только все подузлы одного узла (независимо от уровня вложенности)?
Предусматривается возможность и того и другого сценария. Трудности возникли при загрузке всего содержимого таблицы в граф. S>Если всё дерево — то списком или только корни?
Один из сценариев: при выборе конкретной организации грузится вся структура ее подразделений. S>Т.е. если хотите получить прекэшированное дерево — сначала забираете весь список, делаете его ToList() /*чтобы кэшировать*/ и делаете по этому списку where простым linq to objects. Вуаля
Ну, да. В листинге моего первого сообщения именно так и делается — ToList(). Фрагмент получившегося списка я и привел. C LINQ to Objects поэкспериментирую, спасибо. S>Ещё варианты:
Да, спасибо и за эти идеи. Обязательно посмотрю. S>Да, я бы осторожней подходил к EF. В нынешнем состоянии оно не очень-то подходит для типичных десктопных приложений.
Ну, про десктопные приложения я не писал. Наверное, буду думать в сторону DTO, где в них появится необходимость. S>P.P.S. А у вас подразделения в 1 иерархии могут относиться к нескольким организациям? Во люди живут...
Нет, подразумевалось, что речь идет о нескольких организациях, каждая из которых владеет своим набором подразделений. Что-то не так с схемой таблицы? Почему возник такой вопрос?
Еще раз, большое спасибо за Ваш ответ.
Re[3]: [EF]. Иерархии с нефиксированной глубиной в Entity Fr
Дня alexdr'у!
S>>Т.е. если хотите получить прекэшированное дерево — сначала забираете весь список, делаете его ToList() /*чтобы кэшировать*/ и делаете по этому списку where простым linq to objects. Вуаля A>Ну, да. В листинге моего первого сообщения именно так и делается — ToList(). Фрагмент получившегося списка я и привел. C LINQ to Objects поэкспериментирую, спасибо.
Упс. Не добавил — linq to obj использовать для выборки только корней (если в лом чистить список ручками ).
A>Ну, про десктопные приложения я не писал. Наверное, буду думать в сторону DTO, где в них появится необходимость.
Здесь реального опыта нет — ничего не посоветую, сорри
S>>P.P.S. А у вас подразделения в 1 иерархии могут относиться к нескольким организациям? Во люди живут... A>Нет, подразумевалось, что речь идет о нескольких организациях, каждая из которых владеет своим набором подразделений. Что-то не так с схемой таблицы? Почему возник такой вопрос?
У вас лёгкая денормализация получается — значимые данные (id организации) дублируются по всему дереву подразделений. С одной стороны некритично (шансов что отделы переведутся в другую организацию нет) и даёт возможность быстрой выборки всех подразделений 1 организации. С другой — такой по сути кэш лучше держать отдельно от данных — например, в воспомогательной табличке, обновляемой только по триггеру. Простой пример — нечаянно изменяется айдишник организации только в 1 подразделении. Если не стоит проверка — такое находится только чудом. Поскольку в EF вроде бы нет рид-онли nav props (не гоню, а?) — контроль идёт на откуп клиентам, а тут уж как повезёт.
Если не секрет, как у вас идёт доступ к данным — напрямую к табличкам или через представления/хранимки. Если первое и одни и те же данные будут использоваться несколькими приложениями — я бы задумался.
P.S. Не воспринимайте серьёзно любые советы — никто не знает что у вас за проблема в реальности. Если у вас подразделения — это просто словарик, который в жизни никто не обновит, то всё что я выше написал никакого смысла не имеет.