Как отфильтровать запрос LINQ, чтобы он возвращал только записи с максимальной датой, но при этом получал таблицы, связанные с детьми и внуками?

#c# #linq

Вопрос:

У меня есть запрос LINQ, который возвращает значения, которые я хочу, из таблицы Person, двух связанных дочерних таблиц (Агентские и кадровые сертификаты) и связанной таблицы внуков (тип сертификата). Это работает нормально. Вот запрос LINQ на данный момент (я использую .NET 4.5.2 и EF 6):

 using (var ctx = new AppEntities())
{
    People = ctx.People.AsNoTracking().Where(p => p.Inactive == false)
        .Include(p => p.Agency)
        .Include(p => p.PersonnelCertifications.Select(pc => pc.CertificationType))
        .Where(p => p.PersonnelCertifications.Any(pc => pc.CertificationType.CertType == "Operator"))
        .OrderBy(p => p.LastName)
        .ThenBy(p => p.FirstName)
        .ToList();
}
 

(Люди — это список типов людей.) Однако теперь у меня есть новое требование. В таблице PersonnelCertifications есть столбец с именем CertExpirationDate . Возможно, что может быть 3 записи о персонале, связанные с одним человеком. Например, самым старым может быть 12 сентября 2015 года. Следующий может быть 20 апреля 2021 года. Последнее может быть 12 февраля 2022 года для какого-то человека. При выполнении запроса мне нужна только запись PersonnelCertifications за 12 февраля 2022 года. Как я могу изменить приведенный выше запрос LINQ, чтобы получить максимальную дату сертификации персонала для каждого человека?

Ответ №1:

Простым решением было бы просто загрузить все PersonnelCertifications для a Person из базы данных, а затем отфильтровать самое последнее в памяти, т. е.

 foreach(person in People) {
   person.PersonnelCertifications = person.PersonnelCertifications.Where(pc => pc.CertExpirationDate == person.PersonnelCertifications.Max(pc => pc.CertExpirationDate ) )
}
 

другим вариантом было бы спроецировать сущности в пользовательский класс DTO. Таким образом, вам не нужно загружать дополнительные PersonnelCertifications данные из базы данных, только самые последние, т. е.

 public class PersonViewModel {
   public string Name {get; set;}
   public string Age {get; set;}
   public IEnumerable<PersonalCertificationViewModel> PersonnelCertifications         {get; set;}
}


public class PersonalCertificationViewModel {
    public string Name {get; set;}
    public DateTime ExpirationDate {get; set;}
}

People = ctx.People.AsNoTracking().Where(p => p.Inactive == false)
    .Include(p => p.Agency)
    .Include(p => p.PersonnelCertifications.Select(pc => pc.CertificationType))
    .Where(p => p.PersonnelCertifications.Any(pc => pc.CertificationType.CertType == "Operator"))
    .OrderBy(p => p.LastName)
    .ThenBy(p => p.FirstName)
    .Select(p => new PersonViewModel {
                   Name = p.Name,
                   Age = p.Age,
                   PersonnelCertifications = p.PersonnelCertifications.Where(pc => pc.CertExpirationDate == p.PersonnelCertifications.Max(pc => pc.CertExpirationDate))
                                 .Select(pc => new PersonnelCertifications { Name = pc.Name, ExpirationDate = pc.ExpirationDate })
    }).ToList()
 

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

1. Антон, я решил сначала воспользоваться простым решением, которое ты дал. Просто к вашему сведению, я обнаружил, что мне пришлось использовать ToList() для присвоения отфильтрованного значения person.PersonnelCertifications .