Pergunta

Eu recebi a versão de porta -malas do NH e FNH. Quando tento adicionar o cache do 2º nível, algumas partes do Nibernate se esquecem do Sqldialect escolhido.


Configuração inicial:

var cfg = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(connectionString)
    .DefaultSchema("dbo")
    .UseReflectionOptimizer()    
  .Mappings(m => ................);

Consulta personalizada culpada:

var sql = @"with Foo(col1,col2,col3)
              as (select bla bla bla...)
            Select bla bla bla from Foo";

list = Session.CreateSQLQuery(sql)
  .AddEntity("fizz", typeof(Fizz))
  .SomethingUnimportant();

Quando eu mudo a configuração para:

var cfg = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(connectionString)
    .DefaultSchema("dbo")
     .UseReflectionOptimizer()
     .Cache(c=>c
       .UseQueryCache()
         .ProviderClass<HashtableCacheProvider>())
       .ShowSql())
     .Mappings(m => ................);

Consulta joga erro (WITH Cláusula foi adicionada no MSSQL2008):

A consulta deve começar com 'selecionar' ou 'selecionar distinto'

NotSupportEdException: a consulta deve começar com 'selecionar' ou 'selecionar distintos'] Nibernate.dialect.msql2000dialect.getAfterElectInsertPoint (sqlString sql) +179 Nibernate.dialect.msSql2000Dialect.GetInString (sqLeery cerkerString cerimnate.dialect.msql2000Dialect.getInString (sqLeery Querida .Dialect.MsSql2005Dialect.GetLimitString(SqlString querySqlString, Int32 offset, Int32 last) +127 NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session) +725 NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters , Boolean ReturnProxies) +352 Nibernate.loader.loader.DoQueryAndinitializenonlazyCollections (sessão de isamentoMPlemmentor, consultas de consultas de consultas, devolução booleana) +114 Nibernate.Loadeler.Loader.Dolist (ISessionMemplerorororrororration), sem sesstemer, sem sessão, sem forma, istoRerMerMerMerMerMerArter.


Alguma idéia do que exatamente confunde Nibernate e como corrigi -lo?


Código de Nibernato de culpa (em Nibernate/dialeto/msSQL200Dialect.cs):

private static int GetAfterSelectInsertPoint(SqlString sql)
{
  if (sql.StartsWithCaseInsensitive("select distinct"))
  {
    return 15;
  }
  else if (sql.StartsWithCaseInsensitive("select"))
  {
    return 6;
  }
  throw new NotSupportedException
    ("The query should start with 'SELECT' or 'SELECT DISTINCT'");
  }
}

Parece isso .SetMaxResults(123) causa isso. Felizmente, eu posso iluminar essa consulta.

Espero que isso conserte isso.

Foi útil?

Solução

Eu tive um problema semelhante (removendo SetMaxResults também ajudou, mas eu precisava de paginação) e descobri que a propriedade de configuração da Nibernate a seguir estava causando esse bug:

<property name="use_sql_comments">true</property>

É certamente um bug, porque o GetAfterSelectInsertPoint O método não leva em consideração que os comentários do SQL podem estar antecipados à consulta SQL.

Basta definir o use_sql_comments propriedade para false E o problema desaparece.

Outras dicas

Reparei o bug usando a solução do Alkampfer, mas criei meu próprio dialeto SQL em vez de corrigir a fonte Nibernate diretamente:

public class Sql2008DialectWithBugFixes : MsSql2008Dialect
{
    public override SqlString GetLimitString(SqlString querySqlString, int offset, int last)
    {
        if (offset == 0)
        {
            return querySqlString.Insert(GetAfterSelectInsertPoint(querySqlString), " top " + last);
        }

        return base.GetLimitString(querySqlString, offset, last);
    }

    private static int GetAfterSelectInsertPoint(SqlString sql)
    {
        Int32 selectPosition;

        if ((selectPosition = sql.IndexOfCaseInsensitive("select distinct")) >= 0)
        {
            return selectPosition + 15; // "select distinct".Length;

        }
        if ((selectPosition = sql.IndexOfCaseInsensitive("select")) >= 0)
        {
            return selectPosition + 6; // "select".Length;
        }

        throw new NotSupportedException("The query should start with 'SELECT' or 'SELECT DISTINCT'");
    }
}

Acabei de ter o mesmo problema usando uma consulta semelhante que possui uma cláusula com.

Infelizmente, minha consulta preenche uma grade, com paginação, então eu tenho que manter o SetmaxResults.

Minha solução foi reescrever usando uma tabela derivada:

var sql = @"with Foo(col1,col2,col3)
              as (select x1, x2, x3 from x join y blabla)
            Select col1, col2, col3 from Foo
            join B on B.col1 = Foo.col1";

torna-se

var sql = @"Select col1, col2, col3 from 
           (select x1 as col1, x2 as col2, x3 as col3 
            from x join y blabla) as Foo
           join B on B.col1 = Foo.col1";

Apenas para permitir que Nhibernate insira a string "Top x" após a string "Selecionar" (6 caracteres desde o início) ... sem comentários :(

T

Parece que há algum bug estranho na rotina usada para encontrar o local na consulta para inserir a cláusula superior (getAfterSelectInsertpoint), conforme contado por Sandor. Você pode corrigi -lo diretamente na fonte NH (na verdade, corrigi a versão 2.1 que estou usando em um projeto, você pode Encontre detalhes aqui). Portanto, se você absolutamente precisa ativar comentários com use_sql_comments, você pode :)

Encontrei esse problema ao atualizar de 1,2 para 3,2 (eu sei, grande salto, hein?).

A questão no meu caso foi que existe um espaço líder na frente da instrução SELECT no HQL, por exemplo, String HQL = "Select" ...

Com o dialeto SQL2005, isso trava com uma "System.NotSupportEdException: a consulta deve começar com a mensagem" Selecionar "...".

A solução é

  1. Crie um teste de unidade que falhe, um bom desenvolvedor orientado a teste deve :)
  2. Remova o espaço principal da instrução "Selecionar ..."
  3. Construa e execute o teste de unidade

Assim como eu previ - a seleção não ligada é uma solução alternativa aceitável.

Excluído SetMaxResults E funciona.

Nós encontramos esse problema ao atualizar para o Nibernate versão 3.3, mas por um motivo diferente ... espaço em branco. Tivemos muitas cordas SQL que pareciam assim:

var sql = @"
select col1 from MyTable";

ou:

var sql = @" select col1 from My Table";

Isso resultou na "a consulta deve começar com os erros" selecionar "ou" selecionar distintos "", porque o Nibernate não aparece a string antes de validá -la.

Criamos um novo dialeto que apara a string primeiro para contornar isso:

public class Sql2008DialectCustom : MsSql2008Dialect
{
  public override SqlString GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
  {
    var trimmedQueryString = queryString.Trim();
    return base.GetLimitString(trimmedQueryString, offset, limit);
  }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top