Member 14637786 Ответов: 4

Посмотрите вложенную родителями (самовоспроизведение), пока выполняется условие в EF основной запрос LINQ?


Привет,

существует ли хороший и быстрый способ получить один родительский объект (или его идентификатор) по условию, не повторяя все уровни самореферентных отношений?

Модель, например, такая (псевдокод):

класс MyObject {
bool myCondition;
виртуальный родитель MyObject;
инт атрибутом parentId;
}

А иерархия может быть бесконечной например реальный сценарий:
MyBuilding -> MyFloor -> MyOffice -> MyRoom и т. д.

и то, что мне нужно получить от SQL Db, используя последнее ядро EF, - это первый родитель
это соответствует условию, например, у меня есть MyRoom id = 4, и я хочу получить родительский объект, который имеет myCondition = true, и я понятия не имею, на каком уровне выполняется это условие (может быть истинным уже в MyRoom, может быть истинным в MyBuilding, besically в любом месте пути отношений).

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

Что я уже пробовал:

Я совершенно не представляю, как создать этот запрос, если это вообще возможно?

4 Ответов

Рейтинг:
2

Laxmidhar tatwa technologies

public class MyBuilding
    {
        public int id { get; set; }
        public List<MyFloor> MyFloors { get; set; }
    }
    public class MyFloor
    {
        public int id { get; set; }
        public List<MyOffice> MyOfficerooms { get; set; }
    }
    public class MyOffice
    {
        public int id { get; set; }
        public int MyRoom { get; set; }
    }

 void  travelthroughclass()
        {
            var MyOffices = new List<MyOffice>();
            MyOffices.Add(new MyOffice() { id = 1, MyRoom = 2 });
            MyOffices.Add(new MyOffice() { id = 2, MyRoom = 3 });
            var Floors = new List<MyFloor>();
            Floors.Add(new MyFloor() { id = 1, MyOfficerooms = MyOffices });
            var MyBuildings = new List<MyBuilding>();
            MyBuildings.Add(new MyBuilding() { id = 1, MyFloors = Floors });

            var myoffrooms = (from b in MyBuildings.Select(f => f.MyFloors.Select(r => r.MyOfficerooms.Where(o=>o.id ==2)))

                             select b).ToList();
                               
        }


Member 14637786

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

BillWoodruff

подумайте, прежде чем ответить !

Laxmidhar tatwa technologies

Спасибо братан

Рейтинг:
1

Richard Deeming

Вы можете изменить структуру таблицы? Если да, то вы могли бы использовать HierarchyID колонка.
hierarchyid (Transact-SQL) - SQL Server | Microsoft Docs[^]

Он не поддерживается изначально, но есть сторонняя библиотека, которая добавляет поддержку EF Core 3.1 или более поздней версии:
GitHub - efcore/EFCore.SqlServer.Типа hierarchyid: добавлена поддержка типа данных hierarchyid основной поставщик SQL сервер эф [^]

Тогда вы могли бы использовать IsDescendantOf и GetLevel чтобы найти ближайшего подходящего родителя, который должен выдать довольно приличный запрос:

class MyObject
{
    public HierarchyId Id { get; set; }
    public bool myCondition { get; set; }
}

MyObject targetObject = ...;
HierarchyId targetId = targetObject.Id;

MyObject closestParent = context.MyObjects
    .Where(o => o.myCondition)
    .Where(o => targetId.IsDescendantOf(o.Id))
    .OrderByDescending(o => o.GetLevel())
    .FirstOrDefault();
NB: x.IsDescendantOf(x) будет возвращать true, так что это будет работать, если условие выполняется на целевом объекте.

Недостатком является то, что в настоящее время нет поддержки навигационных свойств:
Поддержка навигационных свойств · выпуск №8 · efcore/EFCore.SqlServer.HierarchyId · GitHub[^]


Рейтинг:
0

#realJSOP

Создайте сохраненный процесс и вызовите его из EF для заполнения коллекции. Пусть запрос в сохраненном proc добавит необходимые столбцы "отношения" и заполнит их. В этот момент ваши дочерние объекты должны иметь ссылку на родительский объект. Пусть база данных делает свою работу.


Member 14637786

К сожалению, мы не храним никакой логики нигде, кроме API :( Спасибо за предложение, Тхо.

Dave Kreskowiak

Это глупое ограничение.

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

Member 14637786

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

Но дело не в этом. Кроме того, довольно легко передать SQL-команду в виде строки TSQL из EF Core в SQL Db, и я думал об этом, но, возможно, есть лучший способ, отсюда и вопрос.

На данный момент рабочее решение заключается в получении всех записей для определенного пути (Include/ThenInclude), а затем вычислении его в памяти, просто углубляясь до тех пор, пока условие не будет выполнено, вместо этого выполняя все соединения на стороне базы данных. Это работает просто отлично, пока пути не вырастут до больших. Это единственное, чего я сейчас боюсь, но мы далеки от реальных жизненных сценариев, подобных этому банкомату.

Я передам идею простой команды TSQL send в БД, но не думаю, что она будет принята.

Также с нетерпением жду любого дальнейшего вклада от любого, кто столкнулся с этой проблемой.

Рейтинг:
0

#realJSOP

Ваша модель не имеет абсолютно никакого смысла.

0) The MyCondition свойство, по-видимому, не имеет никакой связи с указанным вопросом.

1) свойство ParentID должно называться ID, потому что это идентификатор объекта, а не родительский объект..

Предполагая, что EF генерирует частичные классы сущностей (я не знаю, потому что я избегаю EF любой ценой), создайте частичный файл для сущности и добавьте метод к вашей сущности, который получает идентификатор родительского объекта, а когда вы доберетесь до объекта, у которого нет родителя, верните его идентификатор

public int GetTopParentID()
{
    return (this.ParentObject != null) ? this.ParentObject.GetTopParentID() : this.ParentID;
}


Это абсолютно лучший способ сделать это (без необходимости повторения).