Реализация Реляционного Подразделения Entity Framework

У меня есть проблема реляционного разделения, которую я пытаюсь решить в Entity Framework.

У меня есть 2 стола; вещи и вещи.
Каждый материал может иметь много вещей.

Единственный DbSet, с которым я должен работать, — это нормализованный взгляд на вещи и вещи, содержащие ThingId и связанный StuffId.

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

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

Вот мой пример:

public IQueryable<Thing> GetCompleteThingsWhere
                                  (Expression<Func<BetSelection, bool>> conditions)
{
    var filteredThings = this._dbContext.GetQuery<Thing>().Where(conditions);

    var allThings = this._dbContext.GetQuery<Thing>();

    var allThingsForFilteredThings =
        allThings.Where(x => filteredThings.Any(y => y.StuffId == x.StuffId));

    var missingThingsFromFilteredThings =
        allThingsForFilteredThings.Where(x => filteredThings
                                               .All(y => y.ThingId != x.ThingId));

    var completeStuffThings =
        filteredThings.Where(x => !missingThingsFromFilteredThings
                                     .Any(y => x.StuffId == y.StuffId));

    return completeStuffThings;
}

Выполнение запроса занимает около 6 минут, и SQL выглядит очень неэффективным, выполняя исходное условие WHERE в подзапросах в предложении WHERE.

Дополнительное разъяснение:

Примерные данные вещей и вещей

 STUFF            THING
__________       ____________________
| StuffId |      | ThingId | StuffId |
|1        |      |1        |1        |
|2        |      |2        |1        |
|         |      |3        |1        |
|         |      |4        |2        |
|         |      |5        |2        |
|         |      |6        |2        |

Скажем, параметр conditions фильтрует набор вещей так, чтобы у нас был i наш набор результатов:

Results
____________________
| ThingId | StuffId |
|1        |1        |
|4        |2        |
|5        |2        |
|6        |2        |

2 вещи, связанные с StuffId 1 нет в наборе результатов, поэтому теперь я хочу удалить все вещи для StuffId 1 из набора результатов, оставив только полный материал

Results
____________________
| ThingId | StuffId |
|4        |2        |
|5        |2        |
|6        |2        |

2 ответа

  1. Мне удалось сократить время выполнения запроса с 6 минут до менее чем за секунду, разбив запрос на меньшие запросы, так что запрос условий выполняется первым и только один раз, предоставляя набор идентификаторов для работы при выполнении реляционного разделения, а не позволяя EF генерировать подзапросы.

    public IQueryable<Thing> GetCompleteThingsWhere
                                  (Expression<Func<BetSelection, bool>> conditions)
    {
        var filteredThings = base.Filter(conjunction)
                               .Select(thing => new { thing.StuffId, thing.ThingId})
                               .ToArray();
    
        var filteredThingsStuffIds = filteredThings
                                        .Select(arg => arg.StuffId)
                                        .Distinct()
                                        .ToArray();
        var filteredThingsThingIds = filteredThings
                                        .Select(arg => arg.ThingId)
                                        .Distinct()
                                        .ToArray();
    
        var allThings = this.GetAll();
    
        var allThingsForFilteredBets =
            allThings.Where(x => filteredThingsStuffIds.Contains(x.StuffId));
    
        var missingThingsFromFilteredThingsStuffIds =
            allThingsForFilteredBets.Where(x => !filteredThingsThingIds.Contains(x.ThingId))
                                    .Select(thing => thing.StuffId)
                                    .ToArray();
    
        var completeBets =
            allThingsForFilteredBets
                   .Where(x => !missingThingsFromFilteredThingsStuffIds.Contains(x.StuffId));
    
        return completeBets;
    }
    
  2. Итак, если я вас понимаю, вы хотите, чтобы все вещи принадлежали вещам, где все эти вещи соответствуют некоторым условиям?

    public IQueryable<Thing> GetCompleteThingsWhere
                                  (Expression<Func<BetSelection, bool>> conditions)
    {
        return this._dbContext.GetQuery<Stuff>().Where(e => e.Things.All(conditions))
                                                .Select(e => e.Things);
    }
    

    Для работы с нормализованным StuffThing (см. комментарии):

    public IQueryable<Thing> GetCompleteThingsWhere
                                  (Expression<Func<BetSelection, bool>> conditions)
    {
        return this._dbContext.GetQuery<StuffThing>()
                              .GroupBy(e => e.StuffId) // Denormalize
                              .Where(g => g.All(conditions))
                              .SelectMany(g => g);
    }