EF — несколько включений для загрузки иерархических данных. Плохая практика?

Мне нужно загрузить иерархическую структуру, чтобы я мог рекурсивно перебирать ее. Активная загрузка необходима для предотвращения множественных запросов к базе данных при обходе дерева. Кажется, все согласны с тем, что вы не можете загружать бесконечные уровни дерева, поэтому я сделал что-то вроде

                var item= db.ItemHierarchies
                    .Include("Children.Children.Children.Children.Children")
                    .Where(x => x.condition == condition)

загрузить 5 уровней детей. Кажется, это делает работу. Мне интересно, в чем недостаток этого? Если его нет, то теоретически могу ли я добавить сюда 50 уровней включений, не замедляя работу?


person Josh    schedule 06.05.2014    source источник
comment
Это съедает память в геометрической прогрессии. Каждое включение будет содержимым всей таблицы. Итак, у вас есть #rows=r; г + г ^ 2 + г ^ 3 + г ^ 4 ...   -  person Keith Payne    schedule 06.05.2014
comment
На этот вопрос нельзя ответить, не зная объема данных и типа доступа к данным. Иногда даже можно прочитать всю базу данных в память. Если речь идет о небольшой иерархии, которая не будет значительно расти, а доступ к данным будет относительно медленным, вперед. Но имейте в виду, что это абсолютно не масштабируемо.   -  person Gert Arnold    schedule 06.05.2014
comment
Иерархия насчитывает 3700 узлов и растет только при регистрации новых клиентов. Маловероятно, что в ближайшие 10 лет число узлов достигнет 10 000, и, вероятно, никогда не превысит 50 000.   -  person Josh    schedule 07.05.2014


Ответы (2)


Ну, честно.. Это очень плохая практика.

Предположим, у вас в корне 50 объектов... и по 50 на каждом уровне.
В итоге вы можете получить 312500000 "капсул" информации.

Теперь можно спросить: «Так что же в этом плохого?!» Я имею в виду, если это то, что требуется, то почему бы этого не сделать..

Правило №1: мы разрабатываем программное обеспечение, которое должны использовать люди.
И дело в том, что ни один человек не способен бросить взгляд на 312500000 элементов информации одновременно и узнать или сделать из этого что-то полезное. (за исключением того, что это не помогает ему или ей смотреть это)

Правило № 2. Пользовательский интерфейс должен основываться на том, что необходимо, а не на том, что возможно.
А поскольку мы уже установили, что отображение 3 125 00000 капсул данных не требуется, нет причин приводить все то сразу.

И теперь вы можете выйти вперед и сказать: «Но мне на самом деле плевать на пользовательский интерфейс! Все, что мне нужно, это повторить эти данные, чтобы обработать некоторую информацию!

В этом случае вы, вероятно, захотите сохранить свои результаты где-нибудь для дальнейшего использования, но это означает, что это пакетное задание... так почему бы не применить к нему правила пакетного задания... например, обработать его поэлементно, что также может дать вам преимущество разделения его между еще большим количеством машин, если это необходимо.

Итак, вы видите... какой бы путь вы ни выбрали, у него не должно быть причин для этого.
(= определение того, что является плохой практикой.)

Обновление:
После прочтения интересных вопросов в комментариях я хотел бы обновить этот ответ дополнительным анализом:

Решение о том, что является плохой практикой, всегда должно основываться на том, что должно быть достигнуто, или какова роль каждой части в системе. В текущей ситуации (после прочтения комментариев) было представлено или подразумевалось, что хранилище данных на самом деле является постоянным носителем для объектов, в отличие от другой концепции, в которой данные являются «сердцем» приложения.

Мы можем определить два типа данных:

1) Центр обработки данных, который используется в приложениях, ориентированных на данные, таких как банки, CRM, ERP, веб-сайты или другие решения на основе услуг.

VS.

2) Носитель данных, который используется в качестве данных для сохранения, когда приложение не активно, например: любой простой файл сохранения приложения или любой файл сохранения игры и т. д.

Основное отличие состоит в том, что доступ к носителю сохраняемости данных должен осуществляться только одним экземпляром приложения в один момент времени. Это означает, что данные не предназначены для совместного использования многими экземплярами. если данные должны быть разделены - мы имеем дело с приложением центра обработки данных.

Если вашему приложению просто нужен носитель для сохранения данных — загрузка всей информации не может считаться плохой практикой — но вам все равно нужно убедиться, что вы не взрываете память. и в этих рамках работы SQL Server может оказаться не тем, что вам нужно, и не лучшим инструментом для использования.

В другом случае приложения, ориентированного на данные, мой первоначальный ответ остается, так как будет плохой практикой приводить всю информацию для каждого экземпляра приложения.

person G.Y    schedule 06.05.2014
comment
Спасибо за исчерпывающий ответ. Я хотел бы предоставить больше информации и посмотреть, не смогу ли я изменить ваше мнение о том, какой путь вы выберете, не должно быть причин для этого. - person Josh; 07.05.2014
comment
Во-первых, дерево, безусловно, никогда не будет иметь более 50 000 узлов, а в настоящее время их всего 3700. Все данные необходимы, потому что мы должны установить, является ли запрошенный элемент предком родительского элемента, и единственный способ сделать это заключается в загрузке всего дерева от родительского элемента и вниз, чтобы увидеть, включен ли в него предок. - person Josh; 07.05.2014
comment
Во-вторых, вы правы в том, что это не данные, которые я пытаюсь отобразить в пользовательском интерфейсе, а для целей расчета. Я также забыл включить оператор Where в исходный вопрос. Таким образом, с оператором Where вы увидите, что этот запрос может быть выполнен для любого уровня иерархии на основе запроса пользователя. Это означает, что мне нужно где-то хранить результаты 3700 пакетных заданий, верно? Кроме того, это страница, которая используется экономно, поэтому я буду запускать ночные пакетные задания для 3700 записей, сохраняя эти результаты, и все это, чтобы избежать, возможно, 1 или 2 неэффективных запросов в день? - person Josh; 07.05.2014
comment
Влияет ли какая-либо из этих сведений на ваше первоначальное заявление? Будете ли вы по-прежнему использовать пакетное задание в этом случае? - person Josh; 07.05.2014
comment
Джош – Очень хорошие очки! Я включу ссылку на эти вопросы в этот ответ. - person G.Y; 07.05.2014
comment
@ Джош, вы можете сохранить (сериализовать) все свои данные в двоичный файл вместо использования SQL с EF, если вам просто нужен какой-то способ сохранить ваши объекты. сериализация может оказаться более эффективной и менее затратной. конечно, сделка заключалась бы в том, чтобы загружать все при запуске и пересохранять все при необходимости. - person G.Y; 08.05.2014

Я рекомендую взглянуть на SQL, который генерируется, когда вы добавляете нетерпеливую загрузку к вашему запросу.

var item= db.ItemHierarchies
    .Include("Children")
    .Include("Children.Children")
    .Include("Children.Children.Children")
    .Include("Children.Children.Children.Children")
    .Include("Children.Children.Children.Children.Children")    

var sql = ((System.Data.Objects.ObjectQuery)  item).ToTraceString()

// http://visualstudiomagazine.com/blogs/tool-tracker/2011/11/seeing-the-sql.aspx

Вы увидите, что SQL быстро становится очень большим и сложным и потенциально может иметь серьезные последствия для производительности. Было бы неплохо ограничить активную загрузку данными, которые, как вы уверены, вам понадобятся, и рассмотреть возможность использования явной загрузки для некоторых связанных сущностей, особенно если вы работаете с подключенными сущностями, и в этом случае вы можете явно загружать свойства коллекции. когда они нужны.

Также обратите внимание, что вам может не понадобиться несколько отдельных включений. Например, следующие элементы должны быть отдельными включениями, поскольку они относятся к отдельным свойствам (виджеты и гаечные ключи) корня.

var item= db.ItemHierarchies
    .Include("Widgets")
    .Include("Spanners.Flanges")

Но следующее не обязательно:

var item= db.ItemHierarchies
    .Include("Widgets") //This isn't necessary.
    .Include("Widgets.Flanges") //This loads both Widges and Flanges.
person JC Ford    schedule 06.05.2014