Как получить список записей, включающий только один дочерний элемент? - PullRequest
0 голосов
/ 20 мая 2019

Примеры моделей.

public class Root
{
    public string Id { get; private set; }
    public ICollection<Child> Children { get; private set; }
}

public class Child
{
    public string Id { get; private set; }
    public string RootId { get; private set; }
    public string Code { get; private set; }
    public string Name { get; private set; }
}

Ограничения.

Ребенок имеет код RootId и свойство как его уникальный ключ.Это означает, что каждому корневому объекту разрешено иметь столько дочерних объектов, если только два или более дочерних объекта содержат один и тот же код.

Пример запроса

Получить все корневые записи с дочерним элементом, код которого равен A100.

Пример списка данных, содержащих два корневых объекта

Root1 with 2 children, one having a code A100 and the other A200.

Root2 with 2 children, one having a code A100 and the other A500.

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

Пример кода

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .ToList();

foreach (var root in records)
{
    foreach (var child in root.Children)
    {
        if (!child.Code == "A100")
        {
            root.Children.Remove(child);
        }
    }
}

В моих моделях установщики свойств настроены как частные в соответствии с принципами DDD.Поэтому я не могу выполнять проекции linq с помощью команды Select () , как показано ниже.

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root{...})
    .ToList();

Использование конструктора также не идеально в моем случае, потому что я устанавливаю состояние каждого объекта Создано во время создания как часть дизайна каждой модели.

Редактировать 1

Я мог бы использовать конструктор в проекции LINQ с помощью Select (), но моя проблема в том, что во всех моих моделях есть свойство, называемое Состояние , где я обновляюсь в разных точках моей модели в зависимости от того, что произошло.В части конструктора я обновляю его до состояния Create , чтобы подразумевать факт создания новой модели.Так что, если я собираюсь создать конструктор просто так, чтобы я мог создать экземпляр модели из базы данных, это привело бы к путанице, потому что я просто извлекаю уже существующую запись из базы данных, и если я собираюсь использовать конструктор,код, во время создания экземпляра помечает модель как Created , что не то, что я хочу, потому что это создаст новое значение в моем дизайне.

Edit 2

Мои извинения за то, что я недостаточно прояснил себя.Моя проблема в этой части запроса.

Часть 1.

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .ToList();

Так что мне не нужно приходить на эту часть.

Часть 2

foreach (var root in records)
{
    foreach (var child in root.Children)
    {
        if (!child.Code == "A100")
        {
            root.Children.Remove(child);
        }
    }
}

Теперь на основе упомянутых мною ограничений.

Ограничение 1. Не используется общедоступные сеттеры, поэтому я не могу использовать это.

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root{...})
    .ToList();

Constaint 2. Не используется конструктор

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root(...))
    .ToList();

Суть в том, есть ли запрос, который я могу использовать, или любой другой метод получения нужных мне записей прямо из базы данных без выполнениявторая часть запроса?

Ответы [ 2 ]

1 голос
/ 20 мая 2019

Попробуйте традиционный LINQ, чтобы вам больше не приходилось удалять потомков вручную и проецировать результат запроса на анонимный объект.

var result = (from root in context.Roots.Include(x => x.Children)
          from child in root.Children
          where child.Code == "A100"
          select new
          {
              Id = root.Id,
              Children = child
          }).ToList();
1 голос
/ 20 мая 2019

Если у вас нет какой-то сортировки в вашем хранилище данных, которую вы можете использовать, вам все равно придется «извлекать» элементы, чтобы просматривать их.И если вам нужна копия ваших данных с результатом вместо изменения ваших context данных, вам нужно какое-то клонирование.Так что, по моему мнению, учитывая ваши ограничения, лучше всего сохранять ссылки только на полученные элементы Root и Child:

var l = new List<Tuple<Root, Child>>();
foreach(var p in context.Roots.Include(x => x.Children))
{
    foreach(var c in p.Children)
    {
        if(c.Code == "A100")
        {
            l.Add(Tuple.Create(p, c));
            break;
        }
    }
}

Таким образом, вы смотрите только на дочерние и корневые элементы.один раз, и только проверяйте детей, пока не найдете свой предмет.Полученный список кортежей содержит ссылки на соответствующие элементы Root и Child без их изменения, поэтому не используйте свойство Children указанных вами элементов Root.

...