Search code examples
c#sql-serverentity-framework-coreazure-sql-databaseef-core-7.0

How do we tie an expensive SQL Server query back to the part of C# / EF Core code that generated it?


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?


Solution

  • What you ask is possible using Query Tags but that will reveal people are using AsEnumerable(), ToList() or ToArray() to cover up unparseable LINQ queries instead of fixing them. You can save time by looking for ToList() or ToArray() 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:

    var myLocation = new Point(1, 2);
    var nearestPeople = (from f in context.People.TagWith("This is my spatial query!")
                         orderby f.Location.Distance(myLocation) descending
                         select f).Take(5).ToList();
    

    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