I\'m trying to make a stackoverflow clone in my own time to learn EF6 and MVC5, i\'m currently using OWin for authentication.
Everything works fine when i have like
I disagree with Ken2k's answer and am surprised that it has as many upvotes as it does.
The code may be fine in the sense that it compiles, but having that many includes is definitely not OK if you care about your queries being performant. See 8.2.2 of MSFT's EF6 Performance Whitepaper:
When we hear performance questions that involve server response time problems, the source of the issue is frequently queries with multiple Include statements.
Taking a look at the TSQL that EF generates from eagerly loading that many navigation properties in one query (via the numerous .Include() statements) will make it obvious why this is no good. You're going to end up with way too many EF generated joins in one query.
Break up your query so that there are no more than 2 .Include() statements per table fetch. You can do a separate .Load() per dataset but you most likely don't need to go that far, YMMV.
var query = ctx.Questions.Where(...);
// Loads Questions, Attachments, Location tables
query.Include(q => q.Attachments)
.Include(q => q.Location)
.Load();
// Loads IdentityUsers Table
query.Select(q => q.CreatedBy).Load();
// Loads Tags
query.Select(q => q.Tags).Load();
// Loads Upvotes and Downvotes
query.Include(q => q.Upvotes)
.Include(q => q.Downvotes)
.Load();
// Assuming Upvotes.CreatedBy and Downvotes.CreatedBy are also an IdentityUser,
// then you don't need to do anything further as the IdentityUser table is loaded
// from query.Select(q => q.CreatedBy).Load(); and EF will make this association for you
Erik mentions that you can use .AsNoTracking(), and I'm not totally sure at what point he is recommending to use this but if you need to consume the resulting entity set with populated navigation properties (for example query
above) you cannot use .AsNoTracking() at this invalidates the association between entities in EF's cache (once again, from 8.2.2 of MSFT's doc):
This [breaking up the EF query] will work only on tracked queries, as we are making use of the ability the context has to perform identity resolution and association fixup automatically.
For added performance if your query is read only, i.e. you are not updating values you can set the following properties on your DbContext
(assuming you eagerly load all required data):
Configuration.LazyLoadingEnabled = false;
Configuration.AutoDetectChangesEnabled = false;
Configuration.ProxyCreationEnabled = false;
Finally your DbContext should have a Per-Request lifetime/scope.
To Ken's point, certainly if your database architecture is a mess running profiler / viewing the execution plan can help you tweak indexes / identify other problems, but before even thinking of opening profiler break up your query limiting the number of .Includes()
per .Load()
and you should see a tremendous speed improvement from this alone.