Ответ 1
MVC-Mini-Profiler - это многопользовательский инструмент, а не встроенный трассированный sql, но также инструмент профилирования.
Использование профилей базы данных mvc-mini-profiler с первым интерфейсом Entity Framework
Интересно, как отслеживать сгенерированный SQL, как DataContext в LinqToSql.
Я также читал статьи о решении EFProviderWrapper в блоге Jaroslaw Kowalski, но он основан на ObjectContext, не работает для DbContext.
Кто-нибудь знает, как это сделать в DbContext?
Спасибо.
MVC-Mini-Profiler - это многопользовательский инструмент, а не встроенный трассированный sql, но также инструмент профилирования.
Использование профилей базы данных mvc-mini-profiler с первым интерфейсом Entity Framework
Самый простой способ с DbContext
и DbSet<T>
- просто использовать ToString()
в IQueryable
, который вы создали. Например:
var query = context.Blogs.Include(b => b.Posts)
.Where(b => b.Title == "AnyTitle");
string sql = query.ToString();
sql
содержит команду SQL, которая будет выдана БД при выполнении запроса.
Я использую инструмент профилирования SQL Server, чтобы точно определить, какой SQL был создан. Существует также http://efprof.com/, но у него довольно высокая цена.
Вы можете использовать метод ObjectQuery.ToTraceString для просмотра команд хранилища (например, операторов SQL). How To в MSDN покажет вам, как его можно использовать.
Обратите внимание, что во многих случаях вы сможете использовать IQueryable (тип возвращаемого значения из методов расширения Linq, таких как IQueryable.Where) для ObjectQuery, чтобы у вас был доступ к методу ToTraceString.
Я нашел это расширение EFTracingProvider для ObjectContext здесь:
http://efwrappers.codeplex.com/
Но пример для ObjectContext не DbContext, чтобы заставить его работать с DbContext выполните в конструкторе следующее:
Public Sub New()
MyBase.New(EFTracingProviderUtils.CreateTracedEntityConnection("MyDbConnection"), True)
Dim context As ObjectContext = CType(Me, IObjectContextAdapter).ObjectContext
context.EnableTracing()
End Sub
Ой и не забудьте установить конфигурацию:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.diagnostics>
<sources>
<source name="EntityFramework.MyDbConnection" switchValue="All" />
</sources>
</system.diagnostics>
Это затем отслеживает все SQL-запросы в ближайшее окно.
Для тех, кто не хочет втягивать стороннюю библиотеку и просто ищет SQL, содержащий параметры (и не расстраивается всем отражением), этот метод расширения захватывает объект InternalQuery и объект ObjectQuery из DbQuery и возвращает ToTraceString после реализации параметров обратно в строку. Если это не удается, верните параметр ToString без параметров из IQueryable:
public static string ToSqlString<TEntity>(this IQueryable<TEntity> queryable) where TEntity : class
{
try
{
var dbQuery = queryable as DbQuery<TEntity>;
// get the IInternalQuery internal variable from the DbQuery object
var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var iq = iqProp.GetValue(dbQuery);
// get the ObjectQuery internal variable from the IInternalQuery object
var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var oq = oqProp.GetValue(iq);
var objectQuery = oq as ObjectQuery<TEntity>;
var sqlString = objectQuery.ToTraceString();
foreach (var objectParam in objectQuery.Parameters)
{
if (objectParam.ParameterType == typeof(string) || objectParam.ParameterType == typeof(DateTime) || objectParam.ParameterType == typeof(DateTime?))
{
sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("'{0}'", objectParam.Value.ToString()));
}
else if (objectParam.ParameterType == typeof(bool) || objectParam.ParameterType == typeof(bool?))
{
bool val;
if (Boolean.TryParse(objectParam.Value.ToString(), out val))
{
sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("{0}", val ? 1 : 0));
}
}
else
{
sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("{0}", objectParam.Value.ToString()));
}
}
return sqlString;
}
catch (Exception)
{
//squash it and just return ToString
return queryable.ToString();
}
}
Выполните свой код, а затем запустите этот запрос, чтобы увидеть последний выполненный SQL.
SELECT deqs.last_execution_time AS [Time], dest.TEXT AS [Query]
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
ORDER BY deqs.last_execution_time DESC
Это небольшое улучшение по сравнению с решением (по @kmk), которое заменяет параметры значениями. Это решение объявляет и назначает параметры:
public static string ToSqlString<TEntity>(this IQueryable<TEntity> queryable) where TEntity : class
{
StringBuilder parametersBuilder = new StringBuilder();
try
{
var dbQuery = queryable as DbQuery<TEntity>;
// get the IInternalQuery internal variable from the DbQuery object
var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var iq = iqProp.GetValue(dbQuery, null);
// get the ObjectQuery internal variable from the IInternalQuery object
var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var oq = oqProp.GetValue(iq, null);
var objectQuery = oq as ObjectQuery<TEntity>;
var sqlString = objectQuery.ToTraceString();
foreach (var objectParam in objectQuery.Parameters)
{
SqlMetaData metadata = SqlMetaData.InferFromValue(objectParam.Value, objectParam.Name);
string sqlType = metadata.TypeName + (metadata.SqlDbType == SqlDbType.NVarChar ? "(" + metadata.MaxLength + ")" : String.Empty);
parametersBuilder.AppendFormat("declare @{0} {1} = '{2}'", objectParam.Name, sqlType, objectParam.Value);
parametersBuilder.AppendLine();
}
parametersBuilder.AppendLine();
return parametersBuilder.ToString() + sqlString;
}
catch (Exception)
{
//squash it and just return ToString
return queryable.ToString();
}
}