I am trying to split a simple HQL query and retrieve the total number of rows as part of the same query.
My request is quite simple ...
var members = UnitOfWork.CurrentSession.CreateQuery(@" select m from ListMember as m join fetch m.Individual as i") .SetFirstResult(pageIndex*pageSize) .SetMaxResults(pageSize) .List<ListMember>();
Individual is displayed as multi-valued in the ListMember class. This works great. Pagination works as expected and generates the following Sql ...
SELECT TOP ( 10 ) DirPeerG1_1_0_, Director1_0_1_, Director2_1_0_, Forename2_0_1_, Surname0_1_ FROM (SELECT listmember0_.DirPeerGrpMemberID as DirPeerG1_1_0_, listmember1_.DirectorKeyID as Director1_0_1_, listmember0_.DirectorKeyId as Director2_1_0_, listmember1_.Forename1 as Forename2_0_1_, listmember1_.Surname as Surname0_1_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM tblMembers listmember0_ inner join tblIndividuals listmember1_ on listmember0_.DirectorKeyId = listmember1_.DirectorKeyID) as query WHERE query.__hibernate_sort_row > 10 ORDER BY query.__hibernate_sort_row
I read this article posted by Ayende called Paged data + Count (*) with NHibernate: a really easy way! so I tried to implement it in my request.
I followed the steps in the article to add a custom HQL function called rowcount() and changed my query to this ...
var members = UnitOfWork.CurrentSession.CreateQuery(@" select m, rowcount() from ListMember as m join fetch m.Individual as i") .SetFirstResult(pageIndex*pageSize) .SetMaxResults(pageSize) .List<ListMember>();
The Sql that is generated is almost correct, however it includes one of the columns leading to this error twice ...
System.Data.SqlClient.SqlException: The column "..." was specified several times for the query.
The created Sql looks like this:
SELECT TOP ( 10 ) col_0_0_, col_1_0_, Director1_0_1_, DirPeerG1_1_0_, Director1_0_1_, Director2_1_0_, Forename2_0_1_, Surname0_1_ FROM (SELECT listmember0_.DirPeerGrpMemberID as col_0_0_, count(*) over() as col_1_0_, listmember1_.DirectorKeyID as Director1_0_1_, listmember0_.DirPeerGrpMemberID as DirPeerG1_1_0_, listmember1_.DirectorKeyID as Director1_0_1_, listmember0_.DirectorKeyId as Director2_1_0_, listmember1_.Forename1 as Forename2_0_1_, listmember1_.Surname as Surname0_1_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM RCMUser.dbo.tblDirPeerGrpMembers listmember0_ inner join RCMAlpha.dbo.tblDirectorProfileDetails listmember1_ on listmember0_.DirectorKeyId = listmember1_.DirectorKeyID) as query WHERE query.__hibernate_sort_row > 10 ORDER BY query.__hibernate_sort_row
For some reason, it includes the Director1_0_1_ column twice in the projection, which causes this error. This Sql is disappointing close to what I would like, and Im hoping the NHibernate expert there can help explain why this will happen.
Offers tried
Thanks to the suggestion from @Jason. I tried this with a minor version of the .List () method to execute a query, but this, unfortunately, also led to the same Sql with a duplicate column ...
var members = UnitOfWork.CurrentSession.CreateQuery(@" select m, rowcount() from ListMember as m join fetch m.Individual as i") .SetFirstResult(pageIndex * pageSize) .SetMaxResults(pageSize) .List() .Cast<Tuple<ListMember, int>>() .Select(x => x.First);
Update
It doesn't look like this would be possible without going to the NH source code. My decision requirements have changed, and I will no longer be responsible for the answer.
So the solution will be either ...
- Use Futures or MultiQuery to execute two statements in the same command: one to retrieve a data page and one common row counter.
- Change your pagination solution to do without a total number of results. For example, continuous scrolling.