I have read somewhere (can't remeber where and how) that NHibernate 3 allows the determination of the total number of records whilst performing a paged query (in one database query). Is this right?
I have this code:
public IEnumerable<X> GetOrganisms(int PageSize, int Page, out int total)
{
var query = (from e in Session.Query<X>() select e).AsQueryable();
return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList();
}
and would like to initialise 'total' as efficiently as possible.
Thanks.
Christian
PS:
Potential 'solution'?:
Total = (int) Session.CreateCriteria<X>()
.SetProjection(Projections.RowCount())
.FutureValue<Int32>().Value;
var query = (from e in Session.Query<X>() select e).AsQueryable();
return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList();
Your potential solution will be handled in one transaction, but will be two db calls. If you must have only one db call, you should use a multiquery/future query as peer suggested. For more information on the future syntax, check out this post: http://ayende.com/blog/3979/nhibernate-futures.
Here are a few ways to accomplish your scenario...
QueryOver (2 db calls):
var query = session.QueryOver<Organism>();
var result = query
.Skip((Page - 1) * PageSize)
.Take(PageSize)
.List();
var rowcount = query.RowCount();
With a sample set of 100 Organisms, and querying for the organisms 11-20, here are the two queries sent to the db:
SELECT TOP (#p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > #p1 ORDER BY query.__hibernate_sort_row;#p0 = 10 [Type: Int32 (0)], #p1 = 10 [Type: Int32 (0)]
SELECT count(*) as y0_ FROM Organism this_
QueryOver (1 db call with Future):
var query = session.QueryOver<Organism>()
.Skip((Page - 1) * PageSize)
.Take(PageSize)
.Future<Organism>();
var result = query.ToList();
var rowcount = session.QueryOver<Organism>()
.Select(Projections.Count(Projections.Id()))
.FutureValue<int>().Value;
Querying for the same set of data as before, this is the query that is generated:
SELECT TOP (#p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > #p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;#p0 = 10 [Type: Int32 (0)], #p1 = 10 [Type: Int32 (0)]
Criteria(1 db call with Future):
var criteria = session.CreateCriteria<Organism>()
.SetFirstResult((Page - 1) * PageSize)
.SetMaxResults(PageSize)
.Future<Organism>();
var countCriteria = session.CreateCriteria<Organism>()
.SetProjection(Projections.Count(Projections.Id()))
.FutureValue<int>().Value;
Again, querying for the same set of data, criteria with future results in the same query:
SELECT TOP (#p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > #p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;#p0 = 10 [Type: Int32 (0)], #p1 = 10 [Type: Int32 (0)]
Notice that all three query styles result in the same exact queries. The future syntax simply allows NHibernate to make one database call rather than two.
If you are using NHibernate 3, I think the most elegant way to handle this is using the new QueryOver syntax. (You used the old NHibernate.Linq syntax in your proposed solution. You are better off to learn the QueryOver syntax instead.)
I don't have enough reputation to comment on CodeProgression's solution above...but the proper ONE DB call using QueryOver w/ Future<> is:
var query = session.QueryOver<Organism>()
.Skip((Page - 1) * PageSize)
.Take(PageSize)
.Future<Organism>();
// var result = query.ToList();
var rowcount = session.QueryOver<Organism>()
.Select(Projections.Count(Projections.Id()))
.FutureValue<int>().Value;
var result = query.ToList();
int iRowCount = rowcount.Value();
Once you execute the .ToList() - It will hit the database. So you'd have to hit the database again to get the rowCount...Which defeats the purpose of Future<>. Do your ToList() AFTER you've done all your .Future<> queries.
I do not think nhibernate 'realizes' the meaning of any query it performs, so determine the total number of rows is not standard queried.
The most efficient way to get the row count is with futures or a IMultiQuery (to get all results in one roundtript to the database)
nhibernate-futures
Related
In Hibernate 3, is there a way to do the equivalent of the following MySQL limit in HQL?
select * from a_table order by a_table_column desc limit 0, 20;
I don't want to use setMaxResults if possible. This definitely was possible in the older version of Hibernate/HQL, but it seems to have disappeared.
This was posted on the Hibernate forum a few years back when asked about why this worked in Hibernate 2 but not in Hibernate 3:
Limit was never a supported clause
in HQL. You are meant to use
setMaxResults().
So if it worked in Hibernate 2, it seems that was by coincidence, rather than by design. I think this was because the Hibernate 2 HQL parser would replace the bits of the query that it recognised as HQL, and leave the rest as it was, so you could sneak in some native SQL. Hibernate 3, however, has a proper AST HQL Parser, and it's a lot less forgiving.
I think Query.setMaxResults() really is your only option.
// SQL: SELECT * FROM table LIMIT start, maxRows;
Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);
If you don't want to use setMaxResults() on the Query object then you could always revert back to using normal SQL.
The setFirstResult and setMaxResults Query methods
For a JPA and Hibernate Query, the setFirstResult method is the equivalent of OFFSET, and the setMaxResults method is the equivalent of LIMIT:
List<Post> posts = entityManager.createQuery("""
select p
from Post p
order by p.createdOn
""")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
The LimitHandler abstraction
The Hibernate LimitHandler defines the database-specific pagination logic, and as illustrated by the following diagram, Hibernate supports many database-specific pagination options:
Now, depending on the underlying relational database system you are using, the above JPQL query will use the proper pagination syntax.
MySQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?
PostgreSQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
SQL Server
SELECT p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS
FETCH NEXT ? ROWS ONLY
Oracle
SELECT *
FROM (
SELECT
row_.*, rownum rownum_
FROM (
SELECT
p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
) row_
WHERE rownum <= ?
)
WHERE rownum_ > ?
The advantage of using setFirstResult and setMaxResults is that Hibernate can generate the database-specific pagination syntax for any supported relational databases.
And, you are not limited to JPQL queries only. You can use the setFirstResult and setMaxResults method seven for native SQL queries.
Native SQL queries
You don't have to hardcode the database-specific pagination when using native SQL queries. Hibernate can add that to your queries.
So, if you're executing this SQL query on PostgreSQL:
List<Tuple> posts = entityManager.createNativeQuery(
SELECT
p.id AS id,
p.title AS title
from post p
ORDER BY p.created_on
""", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
Hibernate will transform it as follows:
SELECT p.id AS id,
p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
Cool, right?
Beyond SQL-based pagination
Pagination is good when you can index the filtering and sorting criteria. If your pagination requirements imply dynamic filtering, it's a much better approach to use an inverted-index solution, like ElasticSearch.
If you don't want to use setMaxResults, you can also use Query.scroll instead of list, and fetch the rows you desire. Useful for paging for instance.
You can easily use pagination for this.
#QueryHints({ #QueryHint(name = "org.hibernate.cacheable", value = "true") })
#Query("select * from a_table order by a_table_column desc")
List<String> getStringValue(Pageable pageable);
you have to pass new PageRequest(0, 1)to fetch records and from the list fetch the first record.
You need to write a native query, refer this.
#Query(value =
"SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
#Param("userId") String userId, #Param("metricId") Long metricId,
#Param("limit") int limit);
String hql = "select userName from AccountInfo order by points desc 5";
This worked for me without using setmaxResults();
Just provide the max value in the last (in this case 5) without using the keyword limit.
:P
My observation is that even you have limit in the HQL (hibernate 3.x), it will be either causing parsing error or just ignored. (if you have order by + desc/asc before limit, it will be ignored, if you don't have desc/asc before limit, it will cause parsing error)
If can manage a limit in this mode
public List<ExampleModel> listExampleModel() {
return listExampleModel(null, null);
}
public List<ExampleModel> listExampleModel(Integer first, Integer count) {
Query tmp = getSession().createQuery("from ExampleModel");
if (first != null)
tmp.setFirstResult(first);
if (count != null)
tmp.setMaxResults(count);
return (List<ExampleModel>)tmp.list();
}
This is a really simple code to handle a limit or a list.
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
criteria.setMaxResults(3);
List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
System.out.println(user.getStart());
}
Below snippet is used to perform limit query using HQL.
Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);
You can get demo application at this link.
You can use below query
NativeQuery<Object[]> query = session.createNativeQuery("select * from employee limit ?");
query.setparameter(1,1);
#Query(nativeQuery = true,
value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(#Param("email") String email);
We are having trouble with the way NHibernate (version 4.0.0.4000 AND 4.0.4.4000 tested) returns duplicate results. In the sample below, I get 566 results (the correct number of results), but only 549 are unique, meaning there are 17 duplicates.
#region Get Record IDs
public IList<string> GetRecordIds(string user, string agency, DateTime utcFrom, DateTime utcTo, SearchDateRangeType dateRangeType, IEnumerable<string> status, IEnumerable<string> billingStatus, IEnumerable<string> qaStatus, IEnumerable<string> transmissionStatus, IEnumerable<string> scheduledTransmissions, int pageSize = -1, int pageNumber = -1)
{
using (ISession session = NHibernateHelper.OpenSession())
{
ICriteria crit = session.CreateCriteria<Metadata>();
var dateDisjunction = Restrictions.Disjunction();
dateDisjunction.Add(Restrictions.Between("IncidentDate", utcFrom, utcTo));
crit.Add(dateDisjunction);
if (string.IsNullOrEmpty(agency) == false)
{
crit.CreateAlias("Ownership._entities.AsIList", "entities");
crit.Add(Restrictions.Eq("entities._entityName._value", agency));
crit.Add(Restrictions.Eq("entities._isDeleted._value", false) || Restrictions.IsNull("entities._isDeleted._value"));
}
crit.AddOrder(Order.Asc(Projections.Property("RecordId")));
crit.SetProjection(Projections.Property("RecordId"));
if (pageSize > 0 && pageNumber > 0)
{
crit.SetFirstResult(pageSize * (pageNumber - 1)).SetMaxResults(pageSize);
}
var ret = crit.List<string>();
return ret;
}
}
#endregion
SQL Sample 1 is the generated first iteration code from NHibernate. Subsequent pages (second page onward) use ROW_NUMBER() OVER. SQL Sample 2 is a manually-created first page, which uses ROW_NUMBER() OVER as if it was a subsequent page. NHibernate has apparently "optimized" away the ROW_NUMBER() OVER for the first page and that seems(?) to be the cause of our issues.
SQL Sample 1: Generated by NHibernate. Causes duplicates.
SELECT
TOP (100) this_.RecordId as y0_
FROM
PcrMetadata this_
inner join
PcrEntities entities1_
on this_.Id=entities1_.ListKey
WHERE
(
this_.IncidentDate between '0001-01-01 00:00:00.0000000' and '9999-01-01 00:00:00.0000000'
)
and entities1_.Name = 'ClientIDNumber'
and (
entities1_.Entities_IsDeleted = 0
or entities1_.Entities_IsDeleted is null
)
SQL Sample 2: Manually created based on NHibernate second page on. Does not cause duplicates.
SELECT
TOP (100) this_.RecordId as y0_
FROM
(SELECT
this_.Record as y0_,
ROW_NUMBER() OVER(
ORDER BY
CURRENT_TIMESTAMP) as __hibernate_sort_row
FROM
PcrMetadata this_
inner join
PcrEntities entities1_
on this_.Id=entities1_.ListKey
WHERE
(
this_.IncidentDate between '0001-01-01 00:00:00.0000000' and '9999-01-01 00:00:00.0000000'
)
and entities1_.Name = 'ClientIDNumber'
and (
entities1_.Entities_IsDeleted = 0
or entities1_.Entities_IsDeleted is null
)) as query
WHERE
query.__hibernate_sort_row > 0 -- CHANGE THIS NUMBER
Am I doing something wrong? Or there anything I can do to force NHibernate to use ROW_NUMBER?
Thanks in advance for any help!
We cannot JOIN collections and apply paging. Because we are getting cartesian product, which is paged (experience described above).
The solution I would suggest, is to (my way NEVER) join collection. To get the similar results, we should:
use subquery to apply WHERE
use fetch batching to later recieve all collection items without 1 + N issue
There is detailed answer about this issue.
see also:
How to Eager Load Associations without duplication in NHibernate?
What is the solution for the N+1 issue in hibernate?
There is more about making result distinct, but this could not help here:
Criteria.DISTINCT_ROOT_ENTITY vs Projections.distinct
I would like to query a table having multiple rows, each with a timestamp with data coming at ten minute intervals. I would like to find the beginning of any missing data, which is where there is not a timestamp equaling the next ten minute interval, like this:
select a.[timestamp]
from [table] as a
where not exists (select 1
from [table] as b
where a.[id] = b.[id]
and b.[timestamp] = dateadd(mi, 10, a.[timestamp]))
order by a.[timestamp]
I have this so far, but I fail to see how to build the query to let me do the b.[timestamp] = dateadd(mi, 10, a.[timestamp]) in the query above:
Table tableAlias = null;
IList<DateTimeOffset> dateTimeOffsets = session.QueryOver(() => tableAlias)
.WithSubquery
.WhereNotExists(QueryOver.Of<Table>()
.Where(x => x.Id == tableAlias.Id)
.And(Restrictions.Eq(Projections.SqlFunction("addminutes",
NHibernateUtil.DateTimeOffset,
new[]
{
Projections.Property("Timestamp"),
Projections.Constant(10)
}),
<insert timestamp property again here>))
.Select(Projections.Constant(1)))
.Select(x => x.Timestamp)
.List<DateTimeOffset>();
I can not get my head round the restriction on the sqlfuntion part - Nhibernate just won't let me do the comparison of the sqlfunction and my timestamp.
I hope I am on the right track with the code above, but please correct me if I'm totally off with my attempt at solving this...
Kind regards
You are on the right track. You need to use Restrictions.EqProperty instead of Restrictions.Eq since you are comparing two projections and not a projection and a constant value.
Also, you can use an Expression to access the TimeStamp property of the inner query instead of using a string.
The following code works for me on Sql Server 2008, but it may require a bit of tweaking for other database engines:
Table a = null;
session.QueryOver<Table>(() => a)
.WithSubquery
.WhereNotExists(
QueryOver.Of<Table>()
.Where(t => t.Id == a.Id)
.And(Restrictions.EqProperty(
Projections.SqlFunction(
"addminutes",
NHibernateUtil.DateTimeOffset,
Projections.Constant(10),
Projections.Property(() => a.TimeStamp)),
Projections.Property(() => a.TimeStamp)))
.Select(Projections.Constant(1)))
.Select(t => t.TimeStamp)
.List<DateTimeOffset>();
Which should generate the following SQL (at least on Sql Server 2008):
SELECT this_.TimeStamp as y0_
FROM [Table] this_
WHERE not exists (SELECT 1 /* #p0 */ as y0_
FROM [Table] this_0_
WHERE this_0_.Id = this_.Id
and dateadd(minute, 10 /* #p1 */, this_.TimeStamp) = this_.TimeStamp)
I am trying to paginate a simple query using HQL, and retrieve the total row count as part of the same query.
My query is simple enough...
var members = UnitOfWork.CurrentSession.CreateQuery(#"
select m
from ListMember as m
join fetch m.Individual as i")
.SetFirstResult(pageIndex*pageSize)
.SetMaxResults(pageSize)
.List<ListMember>();
The Individual is mapped as a many-to-one on the ListMember class. This works great. The pagination works as expected and generates the following Sql...
SELECT TOP ( 10 /* #p0 */ ) 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 /* #p1 */
ORDER BY query.__hibernate_sort_row
I read this article posted by Ayende called Paged data + Count(*) with NHibernate: The really easy way!, so I tried to implement it in my query.
I followed the steps in the article to add the 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 twice resulting in this error...
System.Data.SqlClient.SqlException:
The column '...' was specified
multiple times for 'query'.
The Sql it generates looks like this...
SELECT TOP ( 10 /* #p0 */ )
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 /* #p1 */
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 frustratingly close to what I would like, and I’m hoping an NHibernate expert out there can help explain why this would happen.
Suggestions Tried
Thanks to the suggestion from #Jason . I tried it with the non-generic version of .List() method to execute the query but this unfortunately also produced the same Sql with the 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 is going to be possible without getting into the NH source code. My solution requirements have changed and I am no longer going to pursue the answer.
In summary, the solution would be to either...
Use Futures or MultiQuery to execute two statements in a single command - one to retrieve the page of data and one the total row count.
Modify your pagination solution to do without a total result count - Continuous scrolling for example.
Hmm, one issue is that you're using a ListMember-typed List method. In the example at the page you linked, he uses List() which returns a list of tuples. The first item of your tuple would be a ListMember and the second would be the row count. That List<> might affect your query and would probably throw an exception even if it did return.
Try using:
var tuples = UnitOfWork.CurrentSession.CreateQuery(#"
select m, rowcount()
from ListMember as m
join fetch m.Individual as i")
.SetFirstResult(pageIndex*pageSize)
.SetMaxResults(pageSize)
.List();
var members = tuples.Select<Tuple<ListMember, int>, ListMember>(x => x.Item1);
but I kinda agree with #dotjoe. A MultiQuery might be easier. It's what I use. Here's a a good link about it from the same author you linked to before (Ayende).
Anybody know how to write a LINQ to SQL statement to return every nth row from a table? I'm needing to get the title of the item at the top of each page in a paged data grid back for fast user scanning. So if i wanted the first record, then every 3rd one after that, from the following names:
Amy, Eric, Jason, Joe, John, Josh, Maribel, Paul, Steve, Tom
I'd get Amy, Joe, Maribel, and Tom.
I suspect this can be done... LINQ to SQL statements already invoke the ROW_NUMBER() SQL function in conjunction with sorting and paging. I just don't know how to get back every nth item. The SQL Statement would be something like WHERE ROW_NUMBER MOD 3 = 0, but I don't know the LINQ statement to use to get the right SQL.
Sometimes, TSQL is the way to go. I would use ExecuteQuery<T> here:
var data = db.ExecuteQuery<SomeObjectType>(#"
SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS [__row]
FROM [YourTable]) x WHERE (x.__row % 25) = 1");
You could also swap out the n:
var data = db.ExecuteQuery<SomeObjectType>(#"
DECLARE #n int = 2
SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS [__row]
FROM [YourTable]) x WHERE (x.__row % #n) = 1", n);
Once upon a time, there was no such thing as Row_Number, and yet such queries were possible. Behold!
var query =
from c in db.Customers
let i = (
from c2 in db.Customers
where c2.ID < c.ID
select c2).Count()
where i%3 == 0
select c;
This generates the following Sql
SELECT [t2].[ID], [t2]. --(more fields)
FROM (
SELECT [t0].[ID], [t0]. --(more fields)
(
SELECT COUNT(*)
FROM [dbo].[Customer] AS [t1]
WHERE [t1].[ID] < [t0].[ID]
) AS [value]
FROM [dbo].[Customer] AS [t0]
) AS [t2]
WHERE ([t2].[value] % #p0) = #p1
Here's an option that works, but it might be worth checking that it doesn't have any performance issues in practice:
var nth = 3;
var ids = Table
.Select(x => x.Id)
.ToArray()
.Where((x, n) => n % nth == 0)
.ToArray();
var nthRecords = Table
.Where(x => ids.Contains(x.Id));
Just googling around a bit I haven't found (or experienced) an option for Linq to SQL to directly support this.
The only option I can offer is that you write a stored procedure with the appropriate SQL query written out and then calling the sproc via Linq to SQL. Not the best solution, especially if you have any kind of complex filtering going on.
There really doesn't seem to be an easy way to do this:
How do I add ROW_NUMBER to a LINQ query or Entity?
How to find the ROW_NUMBER() of a row with Linq to SQL
But there's always:
peopleToFilter.AsEnumerable().Where((x,i) => i % AmountToSkipBy == 0)
NOTE: This still doesn't execute on the database side of things!
This will do the trick, but it isn't the most efficient query in the world:
var count = query.Count();
var pageSize = 10;
var pageTops = query.Take(1);
for(int i = pageSize; i < count; i += pageSize)
{
pageTops = pageTops.Concat(query.Skip(i - (i % pageSize)).Take(1));
}
return pageTops;
It dynamically constructs a query to pull the (nth, 2*nth, 3*nth, etc) value from the given query. If you use this technique, you'll probably want to create a limit of maybe ten or twenty names, similar to how Google results page (1-10, and Next), in order to avoid getting an expression so large the database refuses to attempt to parse it.
If you need better performance, you'll probably have to use a stored procedure or a view to represent your query, and include the row number as part of the stored proc results or the view's fields.