Несколько предложений:
- Как отмечает 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, которое даст вамСписок с разделителями-запятыми.
Подумайте с некоторыми из них и посмотрите, не ускоряет ли это немного.