Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core Lazy loading extremely slow

Tags:

c#

ef-core-2.1

I did a small test enabling lazy loading.optionsBuilder.UseLazyLoadingProxies().UseSqlServer(ConnectionString);

(with EF Core 2.1.4)

I am looping through instruments with and without and this is the results I get

Case 1

var instruments = db.instruments.OrderBy(t=>t.id).Include(t=>t.NavPro1).ThenInclude(t=>t.NavPro2).Take(200);

Case 2

var instruments = db.instruments.OrderBy(t=>t.id).Include(t=>t.NavPro1).ThenInclude(t=>t.NavPro2).Take(200);

Then

   foreach (var i in instruments)
   {
        var props = i.NavPro1;
        foreach (var prop in props)
        {
            sbPrintGreeks.AppendLine(prop.NavPro2.Name + " - " + prop.id + " - " + prop.Value);
        }
   }

Takes 7s to get 100k rows without lazy loading

Takes 160s to get 3k rows with lazy loading.

What can be done to get decent performance ?

like image 618
sofsntp Avatar asked Nov 03 '18 14:11

sofsntp


People also ask

How do I stop my EF core from lazy loading?

Disable Lazy Loading To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.

Does EF core support lazy loading?

EF Core will then enable lazy loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from. For example, in the following entities, the Post. Blog and Blog. Posts navigation properties will be lazy-loaded.


2 Answers

Yes, avoid lazy loading. Period.

Problem is - always, going back to every ORM ever built - that if you do lazy loading, every reference is a lazy load. Which is 1+ round trips (one per property, minimum). Separate SQL execution, separate network time. That adds up extremely fast.

Which is why every ORM ever written supports NON lazy loading, in the EF variant via the .Include statements, which either extend the SQL, or generate separate SQL (ef core, tolist to materialize relationships with efficient sql).

If you insist on using lazy loading - as your question implies, there is no magic dust you spread over your code to avoid the implied negatives of lazy loading.

Now, adding to that - any EF Core before 3.0 is as broken as it gets. And yes, that is 3.0 - not even 2.2. See, there are TONS of issues with various parts, including pretty basic LINQ. And performance. At least 2.2 (coming hopefully in a month) should fix some issue. Until then, try using .Include and AsNoTracking - because IIRC there is a performance bug that also may hit you with when loading 200k rows.

like image 164
TomTom Avatar answered Oct 07 '22 22:10

TomTom


This is a general problem that is known as the N+1 problem.

What's happening here is that you have a lot more requests when you use lazy loading.

The case where you use Include, you have one huge request that gives you all the data - or maybe one request per table, so three request in your case. This depends on the exact structure of your data.

In the case with the lazy loading, you have one request for the instruments and for each instrument you have another request for NavPro1. And for each NavPro1 element, you have yet another request for NavPro2.

So, if you have 1000 instruments, and each has 10 NavPro1, you now have 1 + (1000 * (1 + 10)) = 11001 requests instead of a maximum of three requests. That's slow, period.

like image 40
Daniel Hilgarth Avatar answered Oct 07 '22 22:10

Daniel Hilgarth