Кэш NHibernate 2-го уровня, пользовательский запрос, sqldialect
-
20-09-2019 - |
Вопрос
У меня есть магистральные версии NH и FNH.Когда я пытаюсь добавить кэш 2-го уровня, некоторые части NHibernate забывают о выбранном sqldialect.
Начальная конфигурация:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.DefaultSchema("dbo")
.UseReflectionOptimizer()
.Mappings(m => ................);
Виновный пользовательский запрос:
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();
Когда я меняю конфигурацию на:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.DefaultSchema("dbo")
.UseReflectionOptimizer()
.Cache(c=>c
.UseQueryCache()
.ProviderClass<HashtableCacheProvider>())
.ShowSql())
.Mappings(m => ................);
Запрос выдает ошибку (WITH
предложение было добавлено в mssql2008):
Запрос должен начинаться с "SELECT" или "SELECT DISTINCT"
[Исключение NotSupportedException:Запрос должен начинаться с 'SELECT' или 'SELECT DISTINCT'] NHibernate.Диалект.MsSql2000Dialect.GetAfterSelectInsertPoint(SqlString sql) +179 NHibernate.Диалект.MsSql2000Dialect.GetLimitString(SqlString querySqlString, смещение Int32, ограничение Int32) +119 NHibernate.Диалект.MsSql2005Dialect.GetLimitString(SqlString querySqlString, смещение Int32, последнее значение Int32) +127 NHibernate.Загрузчик.Loader.PrepareQueryCommand(параметры запроса queryParameters, логическая прокрутка, ISessionImplementor session) +725 NHibernate.Загрузчик.Loader.DoQuery(ISessionImplementor сессия, параметры запроса queryParameters, логические процедуры возврата) +352 NHibernate.Загрузчик.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor сессия, параметры запроса queryParameters, логические запросы возврата) +114 NHibernate.Загрузчик.Loader.DoList(ISessionImplementor сессия, параметры запроса queryParameters) +205
Есть идеи, что именно смущает nhibernate и как это исправить?
Виновный код NHibernate (в NHibernate/Dialect/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'");
}
}
Выглядит так, что .SetMaxResults(123)
вызывает это.К счастью, я могу отвязать этот запрос.
Надеюсь, это исправит ситуацию.
Решение
У меня была похожая проблема (удаление SetMaxResults
также помогло, но мне нужна была подкачка) и обнаружил, что следующее свойство конфигурации NHibernate вызывало эту ошибку:
<property name="use_sql_comments">true</property>
Это, безусловно, ошибка, потому что GetAfterSelectInsertPoint
метод не учитывает, что комментарии SQL могут добавляться к SQL-запросу.
Просто установите use_sql_comments
собственность на false
и проблема исчезает.
Другие советы
Я исправил ошибку, используя решение Alkampfer, но я создал свой собственный диалект SQL, а не исправлял исходный код NHibernate напрямую:
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'");
}
}
Только что столкнулся с той же проблемой, используя аналогичный запрос, в котором есть предложение WITH.
К сожалению, мой запрос заполняет сетку подкачкой, поэтому я должен сохранить setMaxResults.
Мое решение состояло в том, чтобы переписать с использованием Производной таблицы:
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";
становится
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";
Просто для того, чтобы позволить NHibernate вставить строку "TOP x" после строки "select" (6 символов с начала)...Без комментариев :(
T
Похоже, что есть какая-то странная ошибка в процедуре, используемой для поиска места в запросе для вставки ВЕРХНЕГО предложения (GetAfterSelectInsertPoint ), как сказал Сандор.Вы можете исправить это непосредственно в исходном коде nh (на самом деле я исправил версию 2.1, которую использую в проекте, вы можете подробности можно найти здесь).Итак, если вам абсолютно необходимо включить комментарии с помощью use_sql_comments, вы можете :)
Я столкнулся с этой проблемой при обновлении с 1.2 до 3.2 (я знаю, БОЛЬШОЙ скачок, да?).
Проблема в моем случае заключалась в том, что перед оператором select в hql есть начальный пробел, напримерСтрока hql = " выбрать "...
С диалектом SQL2005 это приводит к сбою с "System.NotSupportedException:Запрос должен начинаться с сообщения "ВЫБРАТЬ"...".
Решение состоит в том, чтобы
- создайте модульный тест, который завершается неудачей, хороший разработчик, ориентированный на тестирование должен :)
- удалите начальный пробел из инструкции "выбрать ...".
- соберите и запустите модульный тест
Как я и предсказывал - неограничивающий выбор является приемлемым обходным путем.
Удалено SetMaxResults
и это работает.
Мы столкнулись с этой проблемой при обновлении до NHibernate версии 3.3, но по другой причине...пробел.У нас было много sql-строк, которые выглядели примерно так:
var sql = @"
select col1 from MyTable";
или:
var sql = @" select col1 from My Table";
Это привело к ошибкам "Запрос должен начинаться с 'SELECT' или 'SELECT DISTINCT'", поскольку NHibernate не обрезает строку перед ее проверкой.
Мы создали новый диалект, который сначала обрезает строку, чтобы обойти эту проблему:
public class Sql2008DialectCustom : MsSql2008Dialect
{
public override SqlString GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
{
var trimmedQueryString = queryString.Trim();
return base.GetLimitString(trimmedQueryString, offset, limit);
}
}