如何知道哪个 Linq 语句在运行时产生了手头的 SQL?

How to know which Linq statement produced the SQL on hand during runtime?

我想以某种方式编写 Entity Framework 或 LINQ to SQL 查询,以便当我在 SQL Server Profiler 中看到 SQL 查询时,我可以快速确定哪个 LINQ 语句产生了 SQL,而无需进入调试器并对其进行跟踪。该应用程序不会使用存储过程,这样可以很容易地通过过程名称进行搜索。

有什么想法吗?有没有一种方法可以在 LINQ 查询中注入带有代码的静态字符串,仅用于识别查询而不影响查询结果?

更新
我添加这个是为了响应日志记录建议。我不想在生产中一直记录 运行 只是为了在出现问题时我需要查看一些 SQL。寻找一种性能成本最低的方法。将一些唯一的字符串附加到每个 linq 语句,以便代码可以很容易地与 SQL 相关联?

可以使用ToTraceString方法查看生成的SQL。或者使用 LINQPad 之类的东西来测试查询。

ToTraceString 文档:http://msdn.microsoft.com/en-us/library/system.data.objects.objectquery.totracestring(v=vs.110).aspx

这真的很奇怪,并且会有很小的性能成本...

但是您可能会做一些事情,例如将不可能的已知标记值与聚集索引进行比较:

var queryId = -123456789;

this.Products
    .Where(p => p.ProductId != -queryId);

生成的 SQL 将包含 Id:

DECLARE @p0 Int = -456789
SELECT ....
FROM [Product] AS [t0]
WHERE [t0].[ProductId] <> @p0

我完全推荐在生产中做这样的事情:D

如果您要比较的列没有索引,您将得到可怕的 table 扫描,这会降低性能。

如果您使用 MiniProfiler,它可以为您提供一个方便的小面板,显示 SQL 在您的请求中的不同位置生成的内容。它包括在查询 运行 处堆栈上所有方法的名称。这与提供 LINQ 查询的完整字符串表示形式并不完全相同,但它实际上可能正是您要查找的内容。

这是一种可用于标记 Entity Framework 查询的扩展方法:

public static class ExtensionMethods
{
  public static IQueryable<T> SetQueryName<T>(this IQueryable<T> source,
    [CallerMemberName] String name = null,
    [CallerFilePath] String sourceFilePath = "",
    [CallerLineNumber] Int32 sourceLineNumber = 0)
  {
    var expr = Expression.NotEqual(Expression.Constant("Query name: " + name), Expression.Constant(null));
    var param = Expression.Parameter(typeof(T), "param");
    var criteria1 = Expression.Lambda<Func<T, Boolean>>(expr, param);

    expr = Expression.NotEqual(Expression.Constant($"Source: {sourceFilePath} ({sourceLineNumber})"), Expression.Constant(null));
    var criteria2 = Expression.Lambda<Func<T, Boolean>>(expr, param);

    return source.Where(criteria1).Where(criteria2);
  }
}

使用方法如下:

context.Table1.SetQueryName().Where(x => x.C1 > 4)

它将使用调用方法名称作为查询名称。

您可以像这样指定另一个名称:

context.Table1.SetQueryName("Search for numbers > 4").Where(x => x.Number > 4)

下面是 SQL 的样子:

SELECT 
    [Extent1].[Number] AS [Number]
    FROM (SELECT 
    [Table1].[Number] AS [Number]
    FROM [dbo].[Table1] AS [Table1]) AS [Extent1]
    WHERE
      (N'Query name: Search for numbers > 4' IS NOT NULL)
      AND
      (N'Source: C:\Code\Projects\MyApp\Program.cs (49)' IS NOT NULL)
      AND ([Extent1].[Number] > 4)