We have an EF4 query that is taking about 10 seconds to complete. The query isn't so complex, but as there are a lot of related tables included, it's taking a long time. I'm trying to speed it up.
The original query looks something like this (shortened for clarity)...
var supportTickets = ctx.SupportTickets
.Include(s => s.System.Customer.Country)
.Include(s => s.System.Site.Address.Country)
// other includes omitted
.OrderByDescending(s => s.ID)
.ToList();
var ticketsList = supportTickets
.Select(CreateSupportTicketListOverview)
.ToList();
CreateSupportTicketListOverview()
is a method that takes the entity and returns a DTO based on it. A shortened version of it looks like this...
private static SupportTicketListOverview CreateSupportTicketListOverview(SupportTicket x)
{
return new SupportTicketListOverview {
ID = x.ID,
SystemNumber = x.System != null ? x.System.SystemNumber : "",
CustomerName = x.System != null && x.System.Customer != null ? x.System.Customer.Name : "",
ShortSummary = x.ShortSummary,
SiteName = x.Site != null ? x.Site.SiteName : "",
Status = x.Status != null ? x.Status.Description : "",
// other properties omitted for clarity
};
}
As I said, this takes about 10 seconds and returns just under 4000 results. SQL Server Profiler shows that the query took about 6.6s. If we copy the SQL that is generated and run that on its own, it only takes about 2 seconds, which confuses me. Why is it so much faster when run on its own? The time needed to create the entities would not be included in the database query would it? If it were, what is the rest of the time spent doing?
I tried to improve this by turning off tracking and pulling just the required data from the database, rather than the full entities. The revised code looks like this (again shortened for clarity)...
var tickets = ((SalesTrackerCRMEntities) getContext()).SupportTickets
.AsNoTracking()
.Include(s => s.System.Customer.Country)
.Include(s => s.System.Site.Address.Country)
.OrderByDescending(s => s.ID)
.Select(t => new {
SystemNumber = t.System != null ? t.System.DHRNumber : "", t.ID,
CustomerName = t.System != null && t.System.Customer != null ? t.System.Customer.Name : "",
SiteName = t.Site != null ? t.Site.SiteName : "",
Status = t.Status != null ? t.Status.Description : "",
// other stuff omitted
})
.AsEnumerable();
var tickets1 =tickets
.Select(t => new SupportTicketListOverview {
ID = t.ID,
SystemNumber = t.SystemNumber,
CustomerName = t.CustomerName,
ShortSummary = t.ShortSummary,
SiteName = t.SiteName,
Status = t.Status,
// other stuff omitted
})
.ToList();
To my surprise, this took about 15 seconds to complete. Looking in the profiler, the database query itself took around 0.7s, ie ten times faster than the original query, but the EF query overall took 50% longer.
So I'm completely confused. I did some searching, but all the advice I found was for things I'm already doing. For example, this blog post gives seven ways to improve EF performance. These include not using the repository pattern (wasn't really sure what he meant here, as he didn't show any example of how to do or not to do it), not using paging (we don't), using projections (we are, at least in the new query), turning off lazy loading (it was off already), turning off tracking (already did) and using indexes on the tables (we already are). The final tip was to reduce the number of queries. Can't see how we can do this here, as we need all the related data.
In summary, the original database query takes 6.6s, and the overall EF query takes 10s. The revised query takes .7s for the database part, but 15s for the overall EF query. All of these are far too long.
Is anyone able to advise how I can speed up the query? Thanks