I’m looking for a reliable strategy that will allow me to relate a performance problem that the Azure portal has highlighted, back to some query in the C# .NET 7 / EF Core 7 app that has generated that query.
In Azure I see that some query is routinely in the top expensive/slow queries
SELECT * FROM SomeMassiveTable
So, there’s no WHERE
clause – I could start hunting through the codebase for all locations where db.SomeMassiveTable
is used and chase them out to see if anywhere is enumerating it without applying a .Where
but I’m curious to know if anyone out there has developed a better approach.
Is there a way of including an intent kind of comment with a query in a way that would show up in a trace?
For example, perhaps when the developer does something like
db.WithIntent("Cache the whole SomeMassiveTable locally into redis").SomeMassiveTable.ToList()
the Azure portal would complain the top query is:
--Cache the whole SomeMassiveTable locally into redis
SELECT * FROM SomeMassiveTable
so we could search that comment and find it in the codebase.
How about a way of grabbing a generated query (interceptors) and logging the stack trace if the query has no WHERE keyword present? Or getting some feedback from the (local dev) SQL instance itself that it’s just run an expensive query, "and here was the stack trace of the C# side that led to it…"?
I can think of various strategies to try, but I’m trying to find out if this is a problem someone has found a good solution for already
In summary, is there a good, easy to implement, built in approach for tracing problematic queries back to the originating code?
3
Answers
What you ask is possible using Query Tags but that will reveal people are using
AsEnumerable()
,ToList()
orToArray()
to cover up unparseable LINQ queries instead of fixing them. You can save time by looking forToList()
orToArray()
in the code.This was a deliberate action because EF Core throws runtime errors when an unparseable LINQ query is used.
To tag queries in the future, you need to add
.TagWith(someMessage)
, eg:That’s not as strong a warning or indicator as EF Core’s exceptions. It will catch queries that tried to bypass it though.
The tags will be included as comments in the query itself. The query can be logged by using SQL Server Extended Events, a lightweight monitoring system that tracks all events, including query statements. SSMS shows such events using the XEvents Profiler extension.
The comments will probably appear in Query Store reports on expensive queries
To tie all EF Core SQL executions back to the calling methods, you can use an interceptor. The benefit of this solution is that you don’t need to explicitly add anything on every
IQueryable
in your project, this will implicitly work for all of them:Registered when you add your
DbContext
:Customise the
AppendCallingStackTrace
method to taste!Another option worthy to try is EntityFrameworkQueryableExtensions.TagWithCallSite Method that creates a tag with source file name and line where method was called. Examples can be found here.