GradientJagger
GradientJagger

Reputation: 44

EF Core Query Performance

I have a query that loads extremely slowly. I think something is very wrong with it, but i dont understand why. In my application i have an entity KeyText:

public class KeyText
    int Id
    Group Group
    string Text

Most other entities have several of these keytexts. Its just a simple way of filling comboboxes. So if i load via ef core like this: (Everything in the ThenInclude lambda is a KeyText)

context.Companies
    .Include(c => c.Adresses).ThenInclude(a => a.AdressType);
    .Include(c => c.Adresses).ThenInclude(a => a.CityType);
    .Include(c => c.Adresses).ThenInclude(a => a.AnotherKeyText);
    .Include(c => c.Adresses).ThenInclude(a => a.DifferentKeyText);

This can, in the real app, load up to 20 seconds for a ridicolously low amount of data. I'm talking like 5 entries in 5 collections.

However if i load like this:

var companies = context.Companies;
companies.adresses = context.Adresses
                        Include(a => a.AdressType)
                        Include(a => a.CityType)
                        Include(a => a.AnotherKeyText)
                        Include(a => a.DifferentKeyText);

It loads instantly. Why is this? Is the first query that wrong?

Upvotes: 1

Views: 278

Answers (2)

Farhad Zamani
Farhad Zamani

Reputation: 5861

Another option to speed up your query in EF is using .AsNoTracking()

var companies = context.Companies.AsNoTracking();
companies.adresses = context.Adresses.AsNoTracking()
                        .Include(a => a.AdressType)
                        .Include(a => a.CityType)
                        .Include(a => a.AnotherKeyText)
                        .Include(a => a.DifferentKeyText)
context.Companies.AsNoTracking()
    .Include(c => c.Adresses).ThenInclude(a => a.AdressType);
    .Include(c => c.Adresses).ThenInclude(a => a.CityType);
    .Include(c => c.Adresses).ThenInclude(a => a.AnotherKeyText);
    .Include(c => c.Adresses).ThenInclude(a => a.DifferentKeyText);

No tracking queries are useful when the results are used in a read-only scenario. They're quicker to execute because there's no need to set up the change tracking information. If you don't need to update the entities retrieved from the database, then a no-tracking query should be used. You can swap an individual query to be no-tracking

By default, queries that return entity types are tracking. Which means you can make changes to those entity instances and have those changes persisted by SaveChanges().

When you load data from Database by default all records has been tracked by ChangeTracker and this will cause a slight drop in speed and memory allocation.

enter image description here

Upvotes: 0

Arman Ebrahimpour
Arman Ebrahimpour

Reputation: 4461

Let assume you have 5 companies, each one has 5 Addresses:

context.Companies
    .Include(c => c.Adresses).ThenInclude(a => a.AdressType);
    .Include(c => c.Adresses).ThenInclude(a => a.CityType);
    .Include(c => c.Adresses).ThenInclude(a => a.AnotherKeyText);
    .Include(c => c.Adresses).ThenInclude(a => a.DifferentKeyText);

This code generates 5*5=25 left join behalf of Sql each one composed of 3 join (Companies * Adresses * types)


var companies = context.Companies;
companies.adresses = context.Adresses
    .Include(a => a.AdressType)
    .Include(a => a.CityType)
    .Include(a => a.AnotherKeyText)
    .Include(a => a.DifferentKeyText);

This code generates 5 left join behalf of Sql each one composed of 2 join (Adresses * types)

So it's obvious that first approach damages your performance significantly.

Anyway try to not use Include as far as you can. Instead load minimum amount of data that you need and with Join and Select expressions.

Upvotes: 1

Related Questions