Home > Net >  How do I speed up this EF query?
How do I speed up this EF query?

Time:08-26

How do I speed up this EntityFramework query? The profiler tells me that most of the time is spent in od.Order with ~5000 calls.

var orderDetails = context.OrderDetails.ToList();

foreach (OrderDetail od in orderDetails)
{
   var date = od.Order.date;

   if (!trPerDay.ContainsKey(date))
   {
       trPerDay.Add(date, od.quantity);
   }
   else
   {
       trPerDay[date]  = od.quantity;
   }
}

enter image description here

Order property is defined like this:

[MetadataType(typeof(OrderDetailMetaData))]
public partial class OrderDetail
{
   public int orderID { get; set; }

   public string productID { get; set; }

   public int quantity { get; set; }

   public bool upgraded { get; set; }


   public virtual Order Order { get; set; }

   public virtual Product Product { get; set; }
}

CodePudding user response:

What you posted loads the entire OrderDetails table in a single query, from a single thread. Then it tries to lazily load each order which results in a separate call to the database.

It's far faster to let the database do the calculations and only load the final results.

In this case it seems the loop is trying to calculate the total order quantity per order date. The SQL query that produces this would be something like :

SELECT Date,SUM(details.Quantity)
FROM Orders inner join OrderDetails details
    on Orders.ID=details.OrderID
GROUP BY Orders.Date

The equivalent in LINQ can be :

var query=context.OrderDetails
                 .GroupBy(d=>d.Order.Date)
                 .Select(g=>new {
                     Date=g.Key,
                     Total=g.Sum(dt=>dt.Quantity)
                  });
var totals=await query.ToListAsync();

or

var totals=await query.ToDictionaryAsync(t=>t.Date,t=>t.Quantity)

In both cases, a GROUP BY query will be generated that calculates the totals by date.

This assumes that Date is what it says - a date. Either a date-typed field in the database, or a datetime without a time component. If it's actually a Date Time, the query will have to be adjusted to use only the date part. Luckily, EF Core maps DateTime.Date to the equivalent SQL function call:

var query=context.OrderDetails
                 .GroupBy(d=>d.Order.Date)
                 .Select(g=>new {
                     Date=g.Key,
                     Total=g.Sum(dt=>dt.Quantity)
                  });
  • Related