31

Me pregunto cómo rastrear SQL generado como DataContext en LinqToSql.En EF 4.1 DbContext cómo rastrear SQL generado

También leí artículos sobre la solución de EFProviderWrapper en el blog de Jaroslaw Kowalski, pero está basado en ObjectContext, no funciona para DbContext.

¿Alguien sabe cómo hacer esto en DbContext?

Gracias.

+2

Esta es realmente una muy buena pregunta. –

+0

Esta es una de esas [funciones] (http://msdn.microsoft.com/en-us/magazine/ee336126.aspx) que Microsoft quiere que gaste mucho más dinero para obtener (Visual Studio Ultimate). Sin embargo, hay algunas bibliotecas de terceros que lo hacen más fácil. –

Respuesta

0

Utilizo la herramienta de creación de perfiles de SQL Server para ver exactamente qué SQL se ha creado. También hay http://efprof.com/ pero tiene un precio bastante alto.

+1

Gracias, conozco esa herramienta. Y estoy buscando una solución de bricolaje. – Chance

0

Usted puede utilizar el método ObjectQuery.ToTraceString para ver los comandos de la tienda (sentencias SQL, por ejemplo). El How To en MSDN le mostrará cómo se puede usar.

Tenga en cuenta que, en muchos casos, podrá convertir un IQueryable (el tipo de devolución de los métodos de extensión Linq como IQueryable.Where) en un ObjectQuery para que tenga acceso al método ToTraceString.

+0

esto no es muy conveniente. Recomiendo esto: http://stackoverflow.com/questions/6550046/using-mvc-mini-profiler-database-profiling-with-entity-framework-code-first/6743941#6743941 – Chance

15

La manera más fácil con DbContext y DbSet<T> es simplemente usar ToString() en el IQueryable que ha creado. Por ejemplo:

var query = context.Blogs.Include(b => b.Posts) 
        .Where(b => b.Title == "AnyTitle"); 

string sql = query.ToString(); 

sql contiene el comando SQL que se emitirá a la base de datos cuando la consulta se ejecuta.

+1

Eso solo ayuda si está haciendo una búsqueda (que devuelve IQueryable). Si está agregando, modificando y eliminando entidades, ¿cómo ve ese sql? – codemonkey

+0

@codemonkey: cierto, funciona solo para consultas. No creo que haya una forma integrada de rastrear * todas las sentencias SQL con DbContext. – Slauma

+0

Vine aquí para hacerle una pregunta a @codemonkey. Todo lo que encuentro muestra lo primero, sin suerte todavía en este último. – Funka

0

me encontré con esta extensión EFTracingProvider para el ObjectContext aquí:

http://efwrappers.codeplex.com/

Pero el ejemplo es para ObjectContext no DbContext, para conseguir que funcione con DbContext hacer lo siguiente en el constructor:

Public Sub New() 
    MyBase.New(EFTracingProviderUtils.CreateTracedEntityConnection("MyDbConnection"), True) 
    Dim context As ObjectContext = CType(Me, IObjectContextAdapter).ObjectContext 
    context.EnableTracing() 
End Sub 

Ah, y recuerde que debe establecer la configuración:

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <system.diagnostics> 
    <sources> 
     <source name="EntityFramework.MyDbConnection" switchValue="All" /> 
    </sources> 
</system.diagnostics> 

Que traza todos los SQL a la ventana inmediata.

0

Para cualquier persona que no desee obtener en una biblioteca de terceros, y simplemente está buscando el SQL que contiene los parámetros (y no está molesto por toda la reflexión), este método de extensión toma el objeto InternalQuery y el objeto ObjectQuery el DbQuery y devuelve el ToTraceString después de realizar nuevamente los Parámetros en la cadena. Si se produce un error que devuelve el ToString sin parámetros de la 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(); 
     } 
    } 
+0

Hay un problema con esta solución cuando hay más de 10 parámetros. Digamos que un parámetro se llama p10. Cuando reemplaza p1 con su valor, termina con SQL no válido. –

0

ejecutar su código y luego ejecutar esta consulta para ver el último SQL ejecutada.

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 
0

Esto es una ligera mejora sobre la solución (por @kmk) que reemplaza los parámetros con valores. Esta solución declara y asigna los parámetros en su lugar:

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(); 
     } 
    } 
Cuestiones relacionadas