For anyone not wanting to pull in a third party library, and is simply looking for the SQL containing the parameters (and isn't upset by all the reflection), this extension method grabs the InternalQuery object and the ObjectQuery object from the DbQuery and returns the ToTraceString after realizing the Parameters back into the string. If it fails it return the parameter-less ToString from the 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();
}
}