简体   繁体   中英

EF optimize complex LINQ query required?

This is a LINQ query that is used to retrieve data from DB with Entity Framework 7. There are couple of .Where(x => x.CollectedMaterial.OrderByDescending(y => y.Repeats).First() . Is there a way to optimize this query? Is there a need to do that or EF optimizes it itself?

 var bs = await db.Building.Include(x => x.CollectedMaterial)
                    .Where(x => x.MaterialIDLocked == BuildingDataLockType.LockedByBuildingInfoSource)
                    .Where(x => x.CollectedMaterial.Count > 0)
                    .Where(
                        x => x.CollectedMaterial.OrderByDescending(y => y.Repeats).First().Repeats >= firstRepeatsCount)
                    .Where(x => x.CollectedMaterial.OrderByDescending(y => y.Repeats).First().MaterialID != x.MaterialID)
                    .ToListAsync();

AFAIK you shouldn't use multiple where clause to filter out a list but rather put the condition inside a single where clause.

EDIT: yeah it seems to be fine to use multiple where clauses as well

Tip: Try to filter out all the data in the query itself don't filter on the in-memory object by getting unnecessary data, maybe even unnecessary fields you could use a projection operation to select only the required fields.

The answers here too say so, I usually go for a balance b/w readability and performance you should try to evaluate how much performance improvement would be required, also try Visual Studio Profiler it has memory, cpu sampling, etc.

If you are writing that code in a get method then you can use the AsNoTracking method on the DbSet which will improve performance because the Entity Framework won't be tracking it using the tracking objects which it creates by default.

Use the Database.Log property on your DbContext object to log the SQL query generated and see by changing the code which query seems complex, this will help you.

You can log in Console App as context.Database.Log = Console.WriteLine;

You can log in any other application to Debug or Trace as context.Database.Log = message => Trace.WriteLine(message);

Since you are working with IQueryable , you can chain you .Where() conditions as much as you want, all you are creating is an expression tree, which will be evaluated later.

When you actually retrieve the data from DB, (like in your example ToList() ), the EF then create actual optimized SQL query.

Then it depends..the best what you can do is to run SQL profiler against your DB and catch and see actual generated query by EF.

AFAIK, entity optimizes everything until the actual db call is realized. In your case, that is when the call to .ToListAsync is made.

EDIT : seeing this comment, it seems you should merge all queries. Still, think of using the variable defition if you can.

Regarding optimization, you may want to merge

.Where( x => x.CollectedMaterial.OrderByDescending(y => y.Repeats).First().Repeats >= firstRepeatsCount)
.Where( x => x.CollectedMaterial.OrderByDescending(y => y.Repeats).First().MaterialID != x.MaterialID)

to

.Where( x => {
    var firstRepeats = x.CollectedMaterial.OrderByDescending(y => y.Repeats).First().Repeats;
    return ( firstRepeats.Repeats >= firstRepeatsCount && 
             firstRepeats.MaterialID != x.MaterialID ) })

(untested, though)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM