I'm investigating slow performance of the app (the page loads 8+ seconds on Azure), and found out interesting thing.
The following code takes about 0.318 seconds to execute
var reservations = db.Reservations.Include(r => r.PickUpLocation);
var reservationsList = reservations.ToList();
But if I load the navigation property manually, it will complete 2.5 times faster (0.128 seconds).
var reservations = db.Reservations.ToList();
var locations = db.Locations.ToList();
foreach (var r in reservations)
{
r.PickUpLocation = locations.FirstOrDefault(l => l.ID == r.PickUpLocationID);
}
For more complicated queries the difference is even higher - up to 20 times (such as 8 seconds vs. 0.8 seconds).
I tried both QueryTrackingBehavior.NoTracking and QueryTrackingBehavior.TrackAll, with no significant effect on performance.
Also I noticed that live Azure is 10x slower then a local machine, and shows higher difference in performance between EF/manual methods.
Is there any way to make EF work faster?
UPDATE: Locations table has 121 rows (6 string columns, 1 boolean and 2 int) Reservations table has 1268 rows
The important part of Reservation model class:
public class Reservation
{
public int ID { get; set; }
...
[ForeignKey("PickUpLocation")]
public int PickUpLocationID { get; set; }
public Location PickUpLocation { get; set; }
...
}
Location model class:
public class Location
{
public int ID { get; set; }
[StringLength(100)]
public string Name { get; set; }
[StringLength(255)]
public string Address { get; set; }
[StringLength(25)]
public string Barangay { get; set; }
[StringLength(25)]
public string City { get; set; }
[StringLength(25)]
public string Province { get; set; }
[StringLength(20)]
public string Country { get; set; }
[Display(Name = "Service Type")]
public LocationServiceType? ServiceType { get; set; }
[Display(Name = "Location Type")]
[Required]
public LocationType? LocationType { get; set; }
public bool IsDeleted { get; set; }
}
When executing the app with verbose logging on, I can see the SQL queries actually performed. EF7 does just a single query:
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [r].[ID], [r].[AssignedUnitID], [r].[AssignedUnitThirdParty], [r].[BookingCode], [r].[Comments], [r].[DailyPriceID], [r].[DamageDescription], [r].[DocumentCountry], [r].[DocumentNumber], [r].[DocumentOwnerName], [r].[DocumentType], [r].[Email], [r].[FirstName], [r].[FlightArrival], [r].[FlightNumber], [r].[HomeAddress], [r].[LastName], [r].[Phone], [r].[PickUpDateTime], [r].[PickUpLocationID], [r].[RequestedVehicleModelID], [r].[RequestedVehicleTypeID], [r].[ReturnDateTime], [r].[ReturnLocationID], [r].[State], [r].[StayingAddress], [r].[ThirdPartyName], [r].[Type], [r].[UserId], [r].[VersionDate], [l].[ID], [l].[Address], [l].[Barangay], [l].[City], [l].[Country], [l].[IsDeleted], [l].[LocationType], [l].[Name], [l].[Province], [l].[ServiceType]
FROM [Reservation] AS [r]
INNER JOIN [Location] AS [l] ON [r].[PickUpLocationID] = [l].[ID]
So the problem must be in how it parses the result.