Recently I came upon a post from Rown Miller in which he created a simple interceptor to log poor performing queries or failing queries, which really seems promising to track down those queries. Although there is already an awesome well-known tool, Glimpse, this helps you track down the server processing time and other informational data with a quick setup. It also logs the queries if your ASP.NET application is using Entity Framework. But it’s limited to Web applications. So what about Windows, WPF, and other standalone apps?
I just extended the interceptor class as a library and included the support to introduce a custom logger to write queries on any target.
By introducing an Interface for logging:
    1. /// <summary>
    2. /// Implement this logger for any custom targets where queries should be logged.
    3. /// </summary>
    4. public interface IQueryLogger
    5. {
    6. void Write(params string[] content);
    7. }
    Check out the original interceptor here. I have tweaked it a little to have a filter for including StackTrace. Here’s the updated class.
    1. public class ExpensiveSqlLoggerInterceptor: DbCommandInterceptor {
    2. private readonly IQueryLogger _queryLogger;
    3. private readonly int _executionMillisecondThreshold;
    4. private readonly bool _includeStackTrace;
    5. public ExpensiveSqlLoggerInterceptor(IQueryLogger logger, int executionMillisecondThreshold, bool enableStackTrace = true) {
    6. _queryLogger = logger;
    7. _executionMillisecondThreshold = executionMillisecondThreshold;
    8. _includeStackTrace = enableStackTrace;
    9. }
    10. public override voidR eaderExecuting(DbCommand command, DbCommandInterceptionContext < DbDataReader > interceptionContext) {
    11. Executing(interceptionContext);
    12. base.ReaderExecuting(command, interceptionContext);
    13. }
    14. public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext < DbDataReader > interceptionContext) {
    15. Executed(command, interceptionContext);
    16. base.ReaderExecuted(command, interceptionContext);
    17. }
    18. public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext < int > interceptionContext) {
    19. Executing(interceptionContext);
    20. base.NonQueryExecuting(command, interceptionContext);
    21. }
    22. public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext < int > interceptionContext) {
    23. Executed(command, interceptionContext);
    24. base.NonQueryExecuted(command, interceptionContext);
    25. }
    26. public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext < object > interceptionContext) {
    27. Executing(interceptionContext);
    28. base.ScalarExecuting(command, interceptionContext);
    29. }
    30. public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext < object > interceptionContext) {
    31. Executed(command, interceptionContext);
    32. base.ScalarExecuted(command, interceptionContext);
    33. }
    34. private void Executing < T > (DbCommandInterceptionContext < T > interceptionContext) {
    35. var timer = new Stopwatch();
    36. interceptionContext.UserState = timer;
    37. timer.Start();
    38. }
    39. private void Executed < T > (DbCommand command, DbCommandInterceptionContext < T > interceptionContext) {
    40. var timer = (Stopwatch) interceptionContext.UserState;
    41. timer.Stop();
    42. if (interceptionContext.Exception != null) {
    43. _queryLogger.Write("FAILED COMMAND",
    44. interceptionContext.Exception.Message,
    45. command.CommandText,
    46. _includeStackTrace ? Environment.StackTrace : string.Empty,
    47. string.Empty,
    48. string.Empty);
    49. } else if (timer.ElapsedMilliseconds >= _executionMillisecondThreshold) {
    50. _queryLogger.Write(
    51. string.Format("SLOW COMMAND ({0} ms)", timer.ElapsedMilliseconds),
    52. command.CommandText,
    53. _includeStackTrace ? Environment.StackTrace : string.Empty,
    54. string.Empty,
    55. string.Empty
    56. );
    57. }
    58. }
    59. }
    Let's say I want to write to Visual Studio Debug window, simply implement the IQueryLogger interface:
    1. /// <summary>
    2. /// Writes the output to the visual studio output window.
    3. /// </summary>
    4. public class OutputWindowLogger: IQueryLogger
    5. {
    6. public void Write(params string[] content)
    7. {
    8. content.ToList().ForEach(data => System.Diagnostics.Trace.WriteLine(data, "Expensive Query log =>"));
    9. }
    10. }
    A quick setup to use the interceptor is just to implement the DbConfiguration class and add the interceptor:
    1. public class CustomConfig: DbConfiguration
    2. {
    3. public CustomConfig()
    4. {
    5. this.AddInterceptor(new ExpensiveSqlLoggerInterceptor(new OutputWindowLogger(), 1, false));
    6. }
    7. }
    That’s it. Now you can run your application and look for the debug window with “Expensive Query Log =>
    Expensive Query Log
    Here’s the complete source code of the EF6Logger library on GitHub.
    Read more articles on .NET Core: