How can I have NHibernate only generate the SQL without executing it?

SqlNhibernateFluent Nhibernate

Sql Problem Overview


I know how to log the SQL to log4net/NLog/trace window at runtime with the show_sql configuration option.

What I'm looking for is a way to give a Query<T>() to NHibernate retrieve the generated SQL.

I've looked through the Persister class, the Drivers, different Interceptors and Events. There are so many places to look, even narrowing down my search would be of great help.

Sql Solutions


Solution 1 - Sql

You can get the generated sql queries without execution with the following methods:

For the NHibernate.Linq queries:

public String GetGeneratedSql(System.Linq.IQueryable queryable, ISession session)
{
    var sessionImp = (ISessionImplementor) session;
    var nhLinqExpression = new NhLinqExpression(queryable.Expression, sessionImp.Factory);
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(nhLinqExpression, null, false, sessionImp.EnabledFilters, sessionImp.Factory);

    return translators[0].SQLString;
}

For Criteria queries:

public String GetGeneratedSql(ICriteria criteria)
{
    var criteriaImpl = (CriteriaImpl) criteria;
    var sessionImpl = (SessionImpl) criteriaImpl.Session;
    var factory = (SessionFactoryImpl) sessionImpl.SessionFactory;
    var implementors = factory.GetImplementors(criteriaImpl.EntityOrClassName);
    var loader = new CriteriaLoader((IOuterJoinLoadable) factory.GetEntityPersister(implementors[0]), factory, criteriaImpl, implementors[0], sessionImpl.EnabledFilters);

    return loader.SqlString.ToString();
}

For QueryOver queries:

public String GetGeneratedSql(IQueryOver queryOver)
{
    return GetGeneratedSql(queryOver.UnderlyingCriteria);
}

For Hql queries:

public String GetGeneratedSql(IQuery query, ISession session)
{
    var sessionImp = (ISessionImplementor)session;
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(query.QueryString, null, false, sessionImp.EnabledFilters, sessionImp.Factory);

    return translators[0].SQLString;
}

Solution 2 - Sql

For NHibernate 5.2 in case you want to see actual DbCommand prepared for query (so you can check both SQL in cmd.CommandText and supplied parameters in cmd.Parameters):

//For LINQ
public IEnumerable<DbCommand> GetDbCommands<T>(IQueryable<T> query, ISession s)
{
	return GetDbCommands(LinqBatchItem.Create(query), s);
}

//For HQL
public IEnumerable<DbCommand> GetDbCommands(IQuery query, ISession s)
{
	return GetDbCommands(new QueryBatchItem<object>(query), s);
}

//For QueryOver
public IEnumerable<DbCommand> GetDbCommands(IQueryOver query, ISession s)
{
	return GetDbCommands(query.RootCriteria, s);
}

//For Criteria (needs to be called for root criteria)
public IEnumerable<DbCommand> GetDbCommands(ICriteria rootCriteria, ISession s)
{
	return GetDbCommands(new CriteriaBatchItem<object>(query), s);
}

//Adapted from Loader.PrepareQueryCommand
private static IEnumerable<DbCommand> GetDbCommands(IQueryBatchItem item, ISession s)
{
	var si = s.GetSessionImplementation();
	item.Init(si);
	var commands = item.GetCommands();
	foreach (var sqlCommand in commands)
	{
		//If you don't need fully prepared command sqlCommand.Query contains SQL returned by accepted answer
		var sqlString = sqlCommand.Query;
		sqlCommand.ResetParametersIndexesForTheCommand(0);
		var command = si.Batcher.PrepareQueryCommand(System.Data.CommandType.Text, sqlString, sqlCommand.ParameterTypes);
		RowSelection selection = sqlCommand.QueryParameters.RowSelection;
		if (selection != null && selection.Timeout != RowSelection.NoValue)
		{
			command.CommandTimeout = selection.Timeout;
		}

		sqlCommand.Bind(command, si);

		IDriver driver = si.Factory.ConnectionProvider.Driver;
		driver.RemoveUnusedCommandParameters(command, sqlString);
		driver.ExpandQueryParameters(command, sqlString, sqlCommand.ParameterTypes);
		yield return command;
	}
}

Solution 3 - Sql

Based on the NHibernate version 3.4 the method for linq expression is:

public String GetGeneratedSql(System.Linq.IQueryable queryable, ISession session)
      {
         var sessionImp = (ISessionImplementor)session;
         var nhLinqExpression = new NhLinqExpression(queryable.Expression,              
                                     sessionImp.Factory);
         var translatorFactory = new ASTQueryTranslatorFactory();
         var translators = translatorFactory.CreateQueryTranslators(nhLinqExpression.Key, nhLinqExpression, null, false,
                                                                sessionImp.EnabledFilters, sessionImp.Factory);

         var sql = translators.First().SQLString;
         var formamttedSql = FormatStyle.Basic.Formatter.Format(sql);
         int i = 0;
         var map = ExpressionParameterVisitor.Visit(queryable.Expression, sessionImp.Factory).ToArray();
         formamttedSql = Regex.Replace(formamttedSql, @"\?", m => map[i++].Key.ToString().Replace('"', '\''));

         return formamttedSql;
      }

Solution 4 - Sql

Here is how to get generated Sql from Hql with NH 5.2 (a breaking change in NH 4.0.4 appeared which makes the Hql part of the top voted solution obsolete):

public string HqlToSql(string hql, ISession session)
{
    var sessionImp = (ISessionImplementor)session;
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(new NHibernate.Hql.StringQueryExpression(hql),
         null, false, sessionImp.EnabledFilters, sessionImp.Factory);
    var hqlSqlGenerator = new HqlSqlGenerator(((QueryTranslatorImpl)translators[0]).SqlAST, sessionImp.Factory);
    hqlSqlGenerator.Generate();
    return hqlSqlGenerator.Sql.ToString();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhometoastView Question on Stackoverflow
Solution 1 - SqlGerardView Answer on Stackoverflow
Solution 2 - SqlRoman ArtiukhinView Answer on Stackoverflow
Solution 3 - SqlFranki1986View Answer on Stackoverflow
Solution 4 - SqlSÿlView Answer on Stackoverflow