Долгосрочный ответ на запрос в ядре ef - PullRequest
0 голосов
/ 18 сентября 2019

Я создаю выбор для транзакций, когда группируем нас по пользователю и дате, и мне также нужно показывать сумматоры.Результат верный, но время обработки этого запроса составляет 60 с.

Я использую ядро ​​ef 2.2 и пытаюсь использовать аналогичное выражение linq, но время ответа одинаково.

public async Task<ListDataPagination<AdvanceIn, TotalizerAdvanceInDate>> AdvanceInDateAsync(int page, int? limit=15)
        {
            IQueryable<AdvanceIn> query;

            var data = new ListDataPagination<AdvanceIn, TotalizerAdvanceInDate>();

            query = Context.SaleTransaction
                .Include(s => s.AdminSale)
                .ThenInclude(a => a.PlaceCategory)
                .Include(s => s.Transaction)
                .Where(s => s.PayIn.Date <= DateTimeOffset.UtcNow.Date.AddDays(6) 
                    && s.Paid == false
                    && s.ProcessingPaid == false
                    && (s.Transaction.Status == TransactionStatus.Authorized || s.Transaction.Status == TransactionStatus.Paid) )
                .GroupBy(s => new { s.AdminSaleId, s.PayIn.Date })
                .Select(s => new AdvanceIn()
                {
                    Count = s.Count(),
                    NetAmountSum = s.Select(t => t.NetAmount).Sum(),
                    GrossAmountSum = s.Select(t => t.Transaction.GrossAmountWithoutSaleDiscount).Sum(),
                    AdminSale = new SaleTransferReadModel {
                        Id = s.Select(t => t.AdminSale).First().Id,
                        PlaceName = s.Select(t => t.AdminSale).First().PlaceName,
                        PlacePhoto = s.Select(t => t.AdminSale).First().PlacePhoto
                    },
                    PayIn = s.Select(t => t.PayIn).First(),
                    SaleTransactionId = String.Join(",", s.Select(x => x.Id.ToString()))
                })
                .OrderBy(s => s.PayIn);

            var list = await query.AsNoTracking().ToListAsync();
            data.Totalizer = new  TotalizerAdvanceInDate {
                Count = list.Sum(t => t.Count),
                NetAmountSum = list.Sum(a => a.NetAmountSum),
                GrossAmountSum = list.Sum(a => a.GrossAmountSum)
            };

            data.Page = page;
            data.TotalItems = list.Count();
            data.TotalPages = data.TotalItems / limit.Value;

            data.Data = query.Skip(limit.Value * page)
                   .Take(limit.Value)
                   .ToList();


            data.PageTotalizer = new TotalizerAdvanceInDate {
                Count = data.Data.Sum(a => a.Count),
                NetAmountSum = data.Data.Sum(a => a.NetAmountSum),
                GrossAmountSum = data.Data.Sum(a => a.GrossAmountSum)
            };

            return data;
        }

Я ожидаю сокращения времени для получения ответа на этот запрос.

1 Ответ

0 голосов
/ 19 сентября 2019

Несколько предложений:

  1. Как отмечает NetMage, ваш первый вызов .ToList() является серьезной проблемой.Это материализация всего набора в память, что означает передачу всех этих данных с DbServer на веб-сервер.Насколько я вижу, это было сделано только для того, чтобы получить счет.

Обновленный код:

query = Context.SaleTransaction
               .Where(s => s.PayIn.Date <= DateTimeOffset.UtcNow.Date.AddDays(6) 
                    && s.Paid == false
                    && s.ProcessingPaid == false
                    && (s.Transaction.Status == TransactionStatus.Authorized || s.Transaction.Status == TransactionStatus.Paid) )
                .GroupBy(s => new { s.AdminSaleId, s.PayIn.Date });
var rowCount = query.Count();

Это выполнит один запрос, чтобы получить количество групп без возвратавсе данные.

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

Далее, после того, как вы вычислите свои пейджинговые цифры из Count:

-

data.Data = await query.Select(s => new AdvanceIn()
    {
       Count = s.Count(),
       NetAmountSum = s.Select(t => t.NetAmount).Sum(),
       GrossAmountSum = s.Select(t => t.Transaction.GrossAmountWithoutSaleDiscount).Sum(),
       AdminSale =  s.AdminSale.OrderBy(/* ?? */).Select(a => new SaleTransferReadModel 
       {
           Id = a.Id,
           PlaceName = a.PlaceName,
           PlacePhoto = a.PlacePhoto
       }).First(),
       PayIn = s.Select(t => t.PayIn).First(), // Nope! don't return an Entity. Get a value or create a ViewModel like SaleTransferReadModel.
       SaleTransactionId = String.Join(",", s.Select(x => x.Id.ToString())) // This might not work for EF2SQL... Consider returning the list of IDs and projecting to a Display String in the ViewModel.
    }).OrderBy(s => s.PayIn)
    .Skip(limit.Value * page)
    .Take(limit.Value)
    .ToListAsync();

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

Ключевые изменения / примечания: неповторите вызов First, чтобы заполнить модель внутреннего вида.Спроецируйте его с Select на основе первой желаемой дочерней записи.При использовании First и т. Д. Вы всегда должны включать OrderBy, потому что вы не можете полагаться на заказы по умолчанию.

Далее, код внедряет Entity, PayIn.Либо возьмите поле из PayIn для возврата, либо спроецируйте его в ViewModel.Вы не хотите встраивать какие-либо сущности в модель представления, потому что когда сериализатор переходит в JSON или что-то подобное, он затрагивает любые свойства PayIn и потенциально отключает отложенную загрузку.

Наконец, будетstring.Join() в списке идентификаторов.Это может работать, но это может не сработать, и ядро ​​EF имеет неприятную привычку выдавать предупреждение, что оно собирается преждевременно материализовать запрос для двухэтапного проецирования, которое наносит ущерб всей цели.Вместо этого я бы Select a List<string> идентификаторов в модели представления, затем выставил бы дополнительное свойство для модели представления, которое делает string.Join().

public ICollection<Guid> SalesTransactionIds { get; set; } // Populate this in your Linq expression...

public string DisplaySalesTransactionIds 
{
   get { return string.Join(",", SalesTransactionIds); }
}

** примечание: если это дает вамлюбое горе о типах, вы можете использовать SalesTransactionIds.Select(x => x.ToString()) внутри Join.

Когда сериализатор сканирует это, представление / сетка должны получить строковое свойство в модели представления с именем DisplaySalesTransactionIds, которое даст вамСписок с разделителями-запятыми.

Подумайте с некоторыми из них и посмотрите, не ускоряет ли это немного.

...