Ядро Entity Framework: загрузка полной иерархии

#c# #entity-framework-core #entity-framework-core-2.1 #entity-framework-core-2.2

#c# #entity-framework-core #entity-framework-core-2.1 #entity-framework-core-2.2

Вопрос:

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

Я хочу преобразовать этот cte, который вернет полную иерархию в виде коллекции.

 var queryString = @"
        ;WITH cte AS (
            SELECT * FROM [dbo].[Folders] _f WHERE _f.[Id] = @id

            UNION ALL

            SELECT _c.* FROM [dbo].[Folders] _c
            INNER JOIN cte _cte
            ON _cte.[Id] = _c.[ParentFolderId]
        )

        SELECT * FROM cte";


return await this.Entities.FromSql(new RawSqlString(queryString), new SqlParameter("id", id)).ToListAsync();
  

во что-то, что каким-то образом загрузит иерархию дочерних элементов в их родителей, сохраняя при этом производительность одного перехода к БД.

 class Folder
{ 
     public int Id { get; set; }
     public int? FolderId { get; set; }
     public Folder Folder { get; set; }
     public IEnumerable<Folder> Children { get; set; }
}
  

Пример иерархии

 - Main (Id: 1 / ParentId: null) 
     - C1 (2/1) 
           - C11 (4/2) 
                  - C111 (7/4)
           - C12 (5/2)
     - C2 (3/1) 
           - C21 (6/3) 
                  - C211 (8/6)
  

Настроенное отношение

 builder.Ignore(prop => prop.Folder);
builder.HasOne(prop => prop.Folder).WithMany(prop => prop.Children).HasForeignKey(fk => fk.FolderId);
  

Комментарии:

1. EF не может сгенерировать этот CTE. Если вы используете его, например, с FromSqlRaw или FromSqlInterpolated , ядро EF может использовать идентификаторы для восстановления иерархии, если объекты DbContext настроены правильно (отношения и т. Д.)

2. Другой вариант — использовать hierarchyid вместо самоссылки и EFCore.SQLServer. Пакет HierarchyId . Выполнение запросов, безусловно, будет проще и быстрее. Даже если в какой-то момент вы решите удалить этот пакет, будет намного проще писать иерархические SQL-запросы с hierarchyid

3. Я не вижу, чтобы FromSqlRaw или FromSqlInterpolated были доступны для версии 2.x EF core. Посмотрю в библиотеку HierarchyId, спасибо.

4. В версии 2.0 это FromSql для обоих, и вы должны быть очень осторожны, если используете интерполированные строки, иначе вы можете получить хорошую SQL-инъекцию. Вот почему в EF Core 3 есть два отдельных метода для интерполированных или параметризованных запросов

Ответ №1:

Если вам нужна вся иерархия в одном запросе, это просто. Просто извлеките все папки, и если отслеживание изменений включено, EF исправит все отношения. Т.Е. если вы просто запустите

 var folders = db.Set<Folder>().ToList();
  

У вас будет целая иерархия со всеми заполненными свойствами навигации.

Комментарии:

1. Не совсем уверен, что вы подразумеваете под извлечением всех папок? Я предполагаю, что вы относитесь к папкам в просматриваемой папке?

Ответ №2:

Вы можете получить всю иерархию с помощью этого запроса:

 var hierarchy = db.Set<Folder>().Include(f => f.Children).ToList();