如何知道哪个 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)
我想以某种方式编写 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)