Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core 3, optimize lots of Include/ThenInclude

I have a query like this

return await _ctx.Activities
            .Include(a => a.Attributes)
            .Include(a => a.Roles)
            .Include(a => a.Bookmarks)
            .Include(a => a.VideoMetas)
                .ThenInclude(vm => vm.Instances)
            .Include(a => a.ImageMetas)
                .ThenInclude(im => im.Instances)
            .Include(a => a.Procedure)
                .ThenInclude(p => p.Attributes)
            .FirstOrDefaultAsync(a => a.Id == id);

Which turns out to be very slow. In EF 6 you can do .Include(v => v.VideoMetas.Select(vm => vm.Instances) which is a bit faster (I guess, haven't looked at SQL Profiler and actual query tbh). How can I optimize that? I can also use EF Plus where it has .IncludeOptimized() but there is no version for .ThenInclude(). I heard I can use .Select instead of .Include() but really not sure how I can handle that in this query.

like image 375
Expressingx Avatar asked May 09 '20 06:05

Expressingx


People also ask

Does AsNoTracking increase performance?

AsNoTracking is used to increase performance, but returning entities instead of view models can affect performance far more.

What is IncludeOptimized?

IncludeOptimized allows you to optimize the query generated by EF by executing multiple smaller queries instead of executing only one monster SQL.

Is EF core fast?

EF Core 6.0 performance is now 70% faster on the industry-standard TechEmpower Fortunes benchmark, compared to 5.0. This is the full-stack perf improvement, including improvements in the benchmark code, the . NET runtime, etc. EF Core 6.0 itself is 31% faster executing queries.

How to add filters in include/theninclude methods in EF Core?

The EF Core 3.0 Currently does not support adding Filters in Include / ThenInclude methods. But this feature will available in the next version of EF Core. You can use the following operators

What is the EF query include optimized feature?

The EF Query Include Optimized feature lets you filter related entities that will be included. However, instead of to make one big query like Query Include Filter, the queries are split into several queries to optimize the performance like EF Core does.

How to include multiple levels of related data in EF Core?

For example, Customer contains a list of invoices and each invoice then contains a list of items. EF Core has a new extension method ThenInclude(). You can drill down thru relationships to include multiple levels of related data using the ThenInclude method.

How to do eager loading in EF Core?

The eager loading in EF Core done via the Include & ThenInclude methods. We need to supply the navigational property of the related entity as the argument. The next version of EF Core will also support the filtering & Ordering of the related data. You can apply on multiple tables and at multiple levels etc.


Video Answer


2 Answers

You'll want to split it into multiple queries, to speed up the performance. You can use explicit loading for this. It's not the prettiest solution, but it works. Hopefully an easier solution will come in EF 5.

I'm guessing a bit on which fields are collections and which are "normal" entries, but something like this:

var activity = await _ctx.Activities.FindAsync(Id);

await context.Entry(activity)
    .Collection(a => a.Attributes)
    .LoadAsync();

await context.Entry(activity)
    .Collection(a => a.Roles)
    .LoadAsync();

await context.Entry(activity)
    .Collection(a => a.Bookmarks)
    .LoadAsync();

await context.Entry(activity)
    .Collection(a => a.VideoMetas)
    .Query()
    .Include(vm => vm.Instances)
    .LoadAsync();
 
await context.Entry(activity)
    .Collection(a => a.ImageMetas)
    .Query()
    .Include(im => im.Instances)
    .LoadAsync();

await context.Entry(activity)
    .Reference(a => a.Procedure)
    .Query()
    .Include(p => p.Attributes)
    .LoadAsync();

return activity;

Update : As of EF Core 5 Microsoft have added .AsSplitQuery() which allows doing this much less verbose with eager loading!

like image 165
Christian Fosli Avatar answered Oct 19 '22 07:10

Christian Fosli


You can not. This is dimensional expansion SQL fights with. Ef 2.2 had what coudl be seen as an attempt to start with this, but they did not get it working and removed it.

Your best chance is to load in multiple (possibly parallel) queries and then stitch the results together in memory. For EF there are libraries to do that - not sure they exist for EfCore. They run the queries as multiple queries.

like image 23
TomTom Avatar answered Oct 19 '22 07:10

TomTom