EF core 2.1 с использованием дубля в проекции - PullRequest
0 голосов
/ 21 октября 2018

Я хочу использовать Take () в проекции, но я не хочу производить N + 1 запрос, кроме того, без Take () в проекции, с которой я столкнулся с проблемой производительности.Я использовал Take () с EF6, но столкнулся с проблемой N + 1 в EF Core.

пример проекции:

source.Select(post => new PostProject
            {
                PostDisableCoins = post.PostDisableCoins
                                    .OrderBy(x=>x.CoinAmount)
                                    .Take(3)
                                    .ToList(),
                WarStartTime = post.WarStartTime,
                WarEndTime = post.WarEndTime,
                WarWinner = post.WarWinner,
                WarDeclarer = post.WarDeclarer
            }); 

Я хочу иметь Take (3) без N + 1,Любое предложение?!?

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Это дефект реализации EF Core 2.1.Ниже приводится обходной путь, но используйте его только в том случае, если у вас действительно есть проблемы с производительностью, потому что для этого требуется сломать абстракцию соединения свойства навигации и использовать ручное соединение, которое, как я всегда говорю, не следует использовать с EF (Core).Также может не работать, если используется для проецирования более чем одной коллекции или как часть более сложного запроса.

Требуется заменить использование свойства навигации по коллекции post.PostDisableCoins на SelectMany с использованием бокового соединения и скрытияоператоры OrderBy / Take (обновление с правильными типами и именами PK / FK):

var postDisableCoinsQuery = source.SelectMany(p =>
    db.Set<PostDisableCoin>()
        .Where(c => c.PostId == p.Id)
        .OrderByDescending(c => c.CoinAmount)
        .Take(3)
);

Затем выполните GroupJoin для него:

var query = 
    from p in source
    join c in postDisableCoinsQuery on p.Id equals c.PostId into postDisableCoins
    select new PostProject
    {
        PostDisableCoins = postDisableCoins.ToList(),
        WarStartTime = p.WarStartTime,
        WarEndTime = p.WarEndTime,
        WarWinner = p.WarWinner,
        WarDeclarer = post.WarDeclarer
    };

При выполнении,приведенное выше приведет к желаемому результату с помощью одного запроса SQL.

0 голосов
/ 21 октября 2018

Обратите внимание на документ, касающийся EF core 2.1 новых функций:

Мы улучшили наш перевод запросов, чтобы избежать выполнения запросов "N + 1" SQL во многих распространенных сценариях виспользование свойства навигации в проекции приводит к объединению данных из корневого запроса с данными из коррелированного подзапроса. Оптимизация требует буферизации результатов из подзапроса, и мы требуем, чтобы вы изменили запрос, чтобы включить новое поведение .

, например:

var query = context.Customers.Select(
    c => c.Orders.Where(o => o.Amount  > 100).Select(o => o.Amount).ToList());

Обратите внимание, где включено .ToList().

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


В вашем случае это может быть:

source.Select(post => new PostProject
            {
                PostDisableCoins = post.PostDisableCoins
                                    .Select(x => x.OrderBy(x=>x.CoinAmount))
                                    .Select(x => x) 
                                    .Take(3)                                  
                                    .ToList(),
                WarStartTime = post.WarStartTime,
                WarEndTime = post.WarEndTime,
                WarWinner = post.WarWinner,
                WarDeclarer = post.WarDeclarer
            }); 
...