Ros*_*oss 9 c# sql-server entity-framework
我有一些问题,我们只在生产中看到一些运行缓慢的查询,我可以在分析器中看到性能不佳的SQL,但是,我不知道如何使用它来追溯生成语句的代码首先,或者甚至可以追溯到EF查询.EF是否有能力识别SQL语句的来源以帮助追踪代码中的问题?
我相信这个问题可能与代码加载悲观相关,即它加载整个结果集然后过滤代码中的列表而不是在SQL中过滤它
旧线程,但您可以实现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) { }
}
Run Code Online (Sandbox Code Playgroud)
然后在 DbContext 构造函数中,将其添加为拦截器:
DbInterception.Add(new QueryOriginInterceptor());
Run Code Online (Sandbox Code Playgroud)
在探查器中,您会看到查询的执行方式如下:
-- Stack:
-- YourApp.Users.GetUser
SELECT
[Project1].[ID] AS [ID],
FROM [dbo].[User]
WHERE [Extend1].[ID] = @p__linq__0
Run Code Online (Sandbox Code Playgroud)
这种方法有一些考虑因素,例如构建堆栈跟踪的性能影响,并且如果从不同位置调用同一函数,可能会缓存多个执行计划。
您可以在存储库或 DataContext(执行查询的常见位置)中执行以下操作来调试写入每个查询。
var data= from item in entity
where item.id = 564564
select item;
Debug.WriteLine(((System.Data.Objects.ObjectQuery)data).ToTraceString());
Run Code Online (Sandbox Code Playgroud)
您可以编写以下代码来说明执行上述查询时的调用堆栈是什么。然后找到您要查找的查询,调用堆栈将告诉您查询的执行位置。
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames();
Run Code Online (Sandbox Code Playgroud)
您可以使用 microsoft Tracing 或 log4net 来记录这些内容,然后轻松找到您的查询。
| 归档时间: |
|
| 查看次数: |
1633 次 |
| 最近记录: |