Фильтрация с помощью EF Core через несколько вложенных объектных отношений

#c# #entity-framework-core #linq-to-entities

#c# #сущность-фреймворк-ядро #linq-to-entities

Вопрос:

У меня есть новое требование для фильтрации отчетов, возвращаемых из запроса, на основе того, что составляет черный список.

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

 var reports = from r in Context.Reports
                          join ax in Context.AuthorizationXref on r.Id equals ax.ReportId
                          join g in Context.Groups on ax.GroupId equals g.Id
                          join ugx in Context.UsersGroupsXref on g.Id equals ugx.GroupId
                          where ugx.UserId == id
                          select r;
  

AuthorizationXref имеет ReportId и groupId.

 public partial class AuthorizationXref
    {
        public int Id { get; set; }
        public int ReportId { get; set; }
        public int GroupId { get; set; }

        public virtual Groups Group { get; set; }
        public virtual Reports Report { get; set; }
    }

public partial class Groups
    {
        public Groups()
        {
            AuthorizationXref = new HashSet<AuthorizationXref>();
            UsersGroupsXref = new HashSet<UsersGroupsXref>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }

        public virtual ICollection<AuthorizationXref> AuthorizationXref { get; set; }
        public virtual ICollection<UsersGroupsXref> UsersGroupsXref { get; set; }
    }
  

Пользователь «многие ко многим» группирует отчеты «многие ко многим» («многие ко многим» выполняется через внешние ссылки).

В то время как здесь я увеличил его на 700 мс, что кажется очень медленным, и я понимаю, что делаю как минимум 4 обращения к БД, поэтому я попытался сделать это, что составило те же данные более или менее, но в ~ 7 раз быстрее:

     var repo = context.Reports
        .Include(x => x.AuthorizationXref)
        .ThenInclude(x => x.Group)
        .ThenInclude(x => x.UsersGroupsXref)
        .ToList();
  

Это занимает около 100 мс, но не выполняет никакой фильтрации по идентификатору пользователя. Эти тесты находятся в разработке и будут только ухудшаться по мере того, как мы переходим в более высокие среды, где пользователям добавляется все больше отчетов. Я знаю, что использование Selects более эффективно, но я не могу найти примеры сложных вложенных выборок «многие ко многим» для объектов.

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

 var asdf = repo.Where(x=>x.AuthorizationXref.Any(y=>y.ReportId==x.Id));
  

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

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

Ответ №1:

Я предполагаю, что у вас есть связь между отчетами и таблицами UserReportFilter, поскольку у вас есть ReportId в таблице UserReportFilter. Приведенное ниже выражение должно работать,

 var reports = context.Reports
    .Include(x => x.AuthorizationXref)
    .ThenInclude(x => x.Group)
    .ThenInclude(x => x.UsersGroupsXref)
    .Include(x => x.UserReportFilters)
    .Where(x => x.AuthorizationXref.Any(y => y.Group.UsersGroupsXref.Any(z => z.UserId==id))
    .ToList();
  

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

1. Я не получаю Group из AuthorizationXref в intellisense, потому что это коллекция… Я должен был быть более ясным, я думаю.

2. можете ли вы также поделиться своим классом AuthorizationXref.cs? вы должны быть в состоянии получить это, если это коллекция

3. Я добавил один из классов, который практически не отличается от другого. Это мой первый серьезный набег на EF и Core, если уж на то пошло. Наличие многочисленных вложенных коллекций никогда не было тем, что мне нужно было делать, и sproc обычно является goto для такого рода вещей, но я старался не прибегать к нему в этом проекте.

4. Intellisense может давать сбои. ThenInclude() но ошибка исчезнет, если вы напишете это правильно. AuthorizationXref.cs кажется правильным для включения Group

5. Ну, это был примерно лучший способ сделать это и фильтр. Но также немного о производительности. Я думал, что попробовал . Любой ранее, но, по-видимому, нет. Теперь это дает мне правильный подсчет и работает со скоростью около ~ 75 мс, что является огромным улучшением. Спасибо. Мне просто нужно убедиться, что он выдает правильные 65 отчетов, но, похоже, так и должно быть. Возможно, вы захотите включить это новое where в ответ для потомков.