[EF]. Иерархии с нефиксированной глубиной в Entity Framework
От: alexdr Россия  
Дата: 09.02.09 22:34
Оценка:
Привет всем!

Я новичок в 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> с приблизительно такой древовидной структурой:


Полужирным курсивом выделены избыточные данные, дублирующиеся как на самом верхнем уровне, так и в качестве детей соответсвующих родителей. Ясно, что в цикле можно удалить из списка все элементы, находящиеся на верхнем уровне, у которых 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
От: Sinix  
Дата: 10.02.09 02:45
Оценка:
Если не секрет — вам надо тупо загрузить всё содержимое таблицы или только все подузлы одного узла (независимо от уровня вложенности)?

Если всё дерево — то списком или только корни?

Изначально проблема в том, что параллельно селекту со всей таблицы оно строит граф объектов — отсюда ваша структура. Как такового дублирования здесь нет — просто ссылки на одно и то же подразделение лежат в списке и в свежепостроенном графе. Так что ничего страшного.

Т.е. если хотите получить прекэшированное дерево — сначала забираете весь список, делаете его 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
От: alexdr Россия  
Дата: 10.02.09 11:07
Оценка:
Большое спасибо за ответ, 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
От: Sinix  
Дата: 11.02.09 01:29
Оценка:
Дня 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. Не воспринимайте серьёзно любые советы — никто не знает что у вас за проблема в реальности. Если у вас подразделения — это просто словарик, который в жизни никто не обновит, то всё что я выше написал никакого смысла не имеет.

Удачи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.