我遇到了一些问题,我们只在生产中看到一些运行缓慢的查询,我可以在分析器中看到效果不佳的SQL,但是,我不知道如何使用它来追溯到首先生成语句的代码,或者甚至可以追溯到EF查询。 EF是否有能力识别SQL语句的来源以帮助追踪代码中的问题?
我认为这个问题可能与代码加载悲观相关,即它加载整个结果集然后过滤代码中的列表而不是在SQL中过滤它
答案 0 :(得分:1)
您可以在Repository或DataContext(执行查询的常见位置)中执行以下操作来调试每个查询的写入。
var data= from item in entity
where item.id = 564564
select item;
Debug.WriteLine(((System.Data.Objects.ObjectQuery)data).ToTraceString());
您可以编写以下代码来说明执行上述查询时的调用堆栈是什么。然后找到您要查找的查询,调用堆栈将告诉您执行查询的位置。
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames();
您可以使用microsoft tracing或log4net记录这些内容,然后轻松找到您的查询。
答案 1 :(得分:0)
您可以在数据库中创建另一个实体(即:DebugEntity),并在实际查询之前/之后对其运行查询。
ctx.DebugEntity.Where(x => x.ID == "myId1"); //myId2, myId3, myId4, whatever helps you locate the LINQ query from the profiler...
这应该出现在SQL分析器中。
答案 2 :(得分:0)
旧线程,但是您可以实现一个DbCommandInterceptor
来构建堆栈跟踪并将其作为注释添加到SQL命令中。这样会将调用的C#函数与事件探查器和Azure中的EF SQL耦合。
应该执行以下操作:
public class QueryOriginInterceptor : IDbCommandInterceptor
{
private const string _sqlCommentToken = "--";
private const string stackLoggerStartTag = _sqlCommentToken + " Stack:";
private bool _shouldLog = false;
public static string StackLoggerStartTag => stackLoggerStartTag;
public QueryOriginInterceptor(bool shouldLog = true)
{
_shouldLog = shouldLog;
}
void AppendStackTraceToSqlCommand(DbCommand command)
{
if (!_shouldLog)
return;
int positionOfExistingCommentStartTag = command.CommandText.IndexOf(stackLoggerStartTag);
if (positionOfExistingCommentStartTag < 0)
{
IEnumerable<string> frames = (new StackTrace())
.GetFrames()
.ToList()
.Select(f => $"{f?.GetMethod()?.ReflectedType?.FullName ?? "[unknown]"}.{f?.GetMethod()?.Name}")
.Where(l => !l.StartsWith("System.") && !l.StartsWith(this.GetType().FullName));
string comment = $"{stackLoggerStartTag}{Environment.NewLine}{_sqlCommentToken} {string.Join($"{Environment.NewLine}{_sqlCommentToken} ", frames)}{Environment.NewLine}";
command.CommandText = $"{Environment.NewLine}{comment}{command.CommandText}";
}
}
void IDbCommandInterceptor.ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) => AppendStackTraceToSqlCommand(command);
void IDbCommandInterceptor.NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) => AppendStackTraceToSqlCommand(command);
void IDbCommandInterceptor.ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) => AppendStackTraceToSqlCommand(command);
void IDbCommandInterceptor.NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { }
void IDbCommandInterceptor.ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
void IDbCommandInterceptor.ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
}
然后在DbContext构造函数中,将其添加为拦截器:
DbInterception.Add(new QueryOriginInterceptor());
在事件探查器中,您将看到正在执行的查询,如下所示:
-- Stack:
-- YourApp.Users.GetUser
SELECT
[Project1].[ID] AS [ID],
FROM [dbo].[User]
WHERE [Extend1].[ID] = @p__linq__0
这种方法有一些考虑因素,例如构建堆栈跟踪的性能下降,如果从不同位置调用相同的函数,则可能会缓存多个执行计划。