NHibernate считает, что порядок по столбцу не выбран
Я пытаюсь получить страницу 2 результатов запроса, это подготовленный оператор SQL-запроса, который строит мой код:
SELECT DISTINCT Contents.*
FROM Contents
INNER JOIN ContentsFilter ON ContentsFilter.ContentId = Contents.ContentId
INNER JOIN Filter ON Filter.Id = ContentsFilter.FilterId
WHERE Contents.[Key] LIKE 'kh_%'
AND Filter.Name IN (:filters)
ORDER BY Contents.Created DESC
Который затем будет paramterised и тому подобное, и SetFetchSize()
а также SetFirstResult()
призваны на построенный IQuery
, На странице 1 это работает нормально, но на странице 2 я получаю следующее исключение:
NHibernate.HibernateException: The dialect was unable to perform paging of a statement that requires distinct results, and is ordered by a column that is not included in the result set of the query.
at NHibernate.Dialect.MsSql2005DialectQueryPager.BuildFromClauseForPagingDistinctQuery(MsSqlSelectParser sqlQuery, SqlStringBuilder result)
at NHibernate.Dialect.MsSql2005DialectQueryPager.PageByLimitAndOffset(SqlString offset, SqlString limit)
at NHibernate.Dialect.MsSql2005DialectQueryPager.PageBy(SqlString offset, SqlString limit)
at NHibernate.Dialect.MsSql2005Dialect.GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
at NHibernate.Dialect.Dialect.GetLimitString(SqlString queryString, Nullable`1 offset, Nullable`1 limit, Parameter offsetParameter, Parameter limitParameter)
at NHibernate.Loader.Loader.TryGetLimitString(Dialect dialect, SqlString queryString, Nullable`1 offset, Nullable`1 limit, Parameter offsetParameter, Parameter limitParameter, SqlString& result)
at NHibernate.Loader.Loader.AddLimitsParametersIfNeeded(SqlString sqlString, ICollection`1 parameterSpecs, QueryParameters queryParameters, ISessionImplementor session)
at NHibernate.Loader.Loader.CreateSqlCommand(QueryParameters queryParameters, ISessionImplementor session)
at NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session)
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer)
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes)
at NHibernate.Loader.Custom.CustomLoader.List(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Impl.SessionImpl.ListCustomQuery(ICustomQuery customQuery, QueryParameters queryParameters, IList results)
at NHibernate.Impl.AbstractSessionImpl.List(NativeSQLQuerySpecification spec, QueryParameters queryParameters, IList results)
at NHibernate.Impl.AbstractSessionImpl.List(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
at NHibernate.Impl.SqlQueryImpl.List()
at MySite.Data.NHibernate.Shell.KnowledgeHubRepository.ExecuteKnowledgeHubQuery(IQuery query) in C:\Work\MySite\src\app\MySite.Data.NHibernate\Shell\KnowledgeHubRepository.cs:line 194
at MySite.Data.NHibernate.Shell.KnowledgeHubRepository.FindByFilter(String[] filters, Int32 page) in C:\Work\MySite\src\app\MySite.Data.NHibernate\Shell\KnowledgeHubRepository.cs:line 174
at Castle.Proxies.Invocations.IKnowledgeHubRepository_FindByFilter.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at MySite.Core.App.Ioc.Interceptors.StopwatchInterceptor.Intercept(IInvocation invocation) in C:\Work\MySite\src\app\MySite.Core\App\Ioc\Interceptors\StopwatchInterceptor.cs:line 11
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.IKnowledgeHubRepositoryProxy.FindByFilter(String[] filters, Int32 page)
at MySite2.Web.Controllers.KnowledgeHubController.Get(Int32 page, String filters) in C:\Work\MySite\src\app\Website\Controllers\KnowledgeHubController.cs:line 119
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at MySite.Core.App.Ioc.WindsorActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) in C:\Work\MySite\src\app\MySite.Core\App\Ioc\WindsorActionInvoker.cs:line 21
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
Я очень четко выбираю *
поэтому я не думаю, что сообщение об исключении может быть правильным, и, конечно же, если я изменю его на SELECT DISTINCT Contents.*, Contents.Created
конечно же, я получаю System.Data.SqlClient.SqlException: The column 'Created' was specified multiple times for 'q_'.
Так что же на самом деле означает это сообщение об ошибке?
Я использую NHibernate 4.0.4.4000 и SQL Server Express 2008 64-bit 10.0.6.
1 ответ
Я полагаю, вы используете ISession.CreateSqlQuery
, Я даже не знал, что NHibernate попытается проанализировать произвольный SQL-запрос для внедрения в него подкачки, но это действительно так.
К сожалению для вас, ваше дело не покрыто. Первая страница работает, потому что она не должна применять смещение, она просто должна вставить верхний оператор после выбора. Это довольно простой случай "разбора и изменения запроса" без выполнения многих проверок. Увидеть PageByLimitOnly
в MsSql2005DialectQueryPager.cs.
Вторая и последующие страницы сдуты, потому что нужно ввести row_number() over (order by yourOrderBy)
where
оператор условия для смещения с SQL Server 2008/2005, который является более сложным.
NHibernate должен переписать порядок, и текущая реализация защищает от переписывания неверного порядка, в зависимости от столбца, отсутствующего в отдельном. Старые версии SQL Server, в которых поддерживались отдельные запросы, упорядоченные по столбцу, который не был выбран, и этот случай приводил к сбою логики подкачки NHibernate для SQL Server 2005/2008. (SQL Server 2000 поддерживал этот отдельный случай, но я думаю, что он был отброшен в SQL Server 2005, так что, возможно, есть еще одна причина, по которой NHibernate делает это действительно.)
Но текущая реализация проверки не учитывает *
, Увидеть BuildFromClauseForPagingDistinctQuery
в MsSql2005DialectQueryPager.cs.
Итак, поскольку вы уже предоставляете свой собственный SQL, почему бы не вставить в него свой собственный оператор подкачки?
В противном случае вам нужно попытаться поддержать ваше дело с помощью pull-запроса на NHibernate и дождаться его слияния и выпуска.
Или, может быть, обновить до SQL 2012 и установить NHibernate диалект на MsSql2012Dialect
: это поддерживает offset fetch
Операторы SQL, которые проще вставлять в произвольный запрос SQL.