web-dev-qa-db-fra.com

Comment puis-je faire en sorte que NHibernate génère uniquement le SQL sans l'exécuter?

Je sais comment enregistrer la fenêtre SQL vers log4net/NLog/trace au moment de l'exécution avec l'option de configuration show_sql.

Ce que je recherche, c'est un moyen de donner une Query<T>() à NHibernate pour récupérer le SQL généré.

J'ai parcouru la classe Persister, les pilotes, les différents intercepteurs et événements. Il y a tellement d'endroits à visiter, même restreindre ma recherche serait d'une grande aide.

52
hometoast

Vous pouvez obtenir les requêtes SQL générées sans exécution avec les méthodes suivantes:

Pour les requêtes NHibernate.Linq:

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;
}

Pour les requêtes de critères:

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

Pour les requêtes QueryOver:

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

Pour les requêtes Hql:

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;
}
98
Gerard

Pour NHibernate 5.2 au cas où vous voudriez voir le DbCommand réel préparé pour la requête (vous pouvez donc vérifier les deux SQL dans cmd.CommandText et les paramètres fournis dans 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
public IEnumerable<DbCommand> GetDbCommands(ICriteria query, 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)
    {
        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;
    }
}
4
Roman Artiukhin

Voici comment obtenir Sql généré à partir de Hql avec NH 5.2 (un changement de rupture dans NH 4.0.4 est apparu qui rend la partie Hql de la solution la mieux votée obsolète):

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();
}
1
Sÿl

Sur la base de la version 3.4 de NHibernate, la méthode d'expression linq est:

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;
      }
1
Franki1986