给定一个 ICriteria 对象形式的查询,我想使用 NHibernate(通过投影?)来查找元素的顺序,其方式等同于使用
SELECT ROW_NUMBER() OVER (...)
在查询中查找特定项目的索引。(我需要这个用于分页中的“跳转到页面”功能)有什么建议吗?
注意:我还不想转到一个给定编号的页面-我知道该怎么做-我想获取该项目的索引,以便可以将其除以页面大小并获取页面索引。
给定一个 ICriteria 对象形式的查询,我想使用 NHibernate(通过投影?)来查找元素的顺序,其方式等同于使用
SELECT ROW_NUMBER() OVER (...)
在查询中查找特定项目的索引。(我需要这个用于分页中的“跳转到页面”功能)有什么建议吗?
注意:我还不想转到一个给定编号的页面-我知道该怎么做-我想获取该项目的索引,以便可以将其除以页面大小并获取页面索引。
在查看了 NHibernate 的源代码后,我相当确定不存在这样的功能。
但是,我不介意有人证明我错了。
在我的特定设置中,我确实通过编写一个采用几个 lambdas 的方法解决了这个问题(代表键列,以及一个可选的列来过滤 - 特定域实体的所有属性)。然后此方法构建 sql 并调用session.CreateSQLQuery(...).UniqueResult();
我并没有声称这是一个通用解决方案。
为了避免使用魔术字符串,我PropertyHelper<T>
从这个答案中借了一份副本。
这是代码:
public abstract class RepositoryBase<T> where T : DomainEntityBase
{
public long GetIndexOf<TUnique, TWhere>(T entity, Expression<Func<T, TUnique>> uniqueSelector, Expression<Func<T, TWhere>> whereSelector, TWhere whereValue) where TWhere : DomainEntityBase
{
if (entity == null || entity.Id == Guid.Empty)
{
return -1;
}
var entityType = typeof(T).Name;
var keyField = PropertyHelper<T>.GetProperty(uniqueSelector).Name;
var keyValue = uniqueSelector.Compile()(entity);
var innerWhere = string.Empty;
if (whereSelector != null)
{
// Builds a column name that adheres to our naming conventions!
var filterField = PropertyHelper<T>.GetProperty(whereSelector).Name + "Id";
if (whereValue == null)
{
innerWhere = string.Format(" where [{0}] is null", filterField);
}
else
{
innerWhere = string.Format(" where [{0}] = :filterValue", filterField);
}
}
var innerQuery = string.Format("(select [{0}], row_number() over (order by {0}) as RowNum from [{1}]{2}) X", keyField, entityType, innerWhere);
var outerQuery = string.Format("select RowNum from {0} where {1} = :keyValue", innerQuery, keyField);
var query = _session
.CreateSQLQuery(outerQuery)
.SetParameter("keyValue", keyValue);
if (whereValue != null)
{
query = query.SetParameter("filterValue", whereValue.Id);
}
var sqlRowNumber = query.UniqueResult<long>();
// The row_number() function is one-based. Our index should be zero-based.
sqlRowNumber -= 1;
return sqlRowNumber;
}
public long GetIndexOf<TUnique>(T entity, Expression<Func<T, TUnique>> uniqueSelector)
{
return GetIndexOf(entity, uniqueSelector, null, (DomainEntityBase)null);
}
public long GetIndexOf<TUnique, TWhere>(T entity, Expression<Func<T, TUnique>> uniqueSelector, Expression<Func<T, TWhere>> whereSelector) where TWhere : DomainEntityBase
{
return GetIndexOf(entity, uniqueSelector, whereSelector, whereSelector.Compile()(entity));
}
}
public abstract class DomainEntityBase
{
public virtual Guid Id { get; protected set; }
}
你像这样使用它:
...
public class Book : DomainEntityBase
{
public virtual string Title { get; set; }
public virtual Category Category { get; set; }
...
}
public class Category : DomainEntityBase { ... }
public class BookRepository : RepositoryBase<Book> { ... }
...
var repository = new BookRepository();
var book = ... a persisted book ...
// Get the index of the book, sorted by title.
var index = repository.GetIndexOf(book, b => b.Title);
// Get the index of the book, sorted by title and filtered by that book's category.
var indexInCategory = repository.GetIndexOf(book, b => b.Title, b => b.Category);
正如我所说,这对我有用。我一定会在前进的过程中对其进行调整。YMMV。
现在,如果 OP 自己解决了这个问题,那么我很想看看他的解决方案!:-)
ICriteria 有这两个功能:
SetFirstResult()
和
SetMaxResults()
它将您的 SQL 语句转换为使用 ROW_NUMBER(在 sql server 中)或在 MySql 中使用限制。
因此,如果您想在第三页上显示 25 条记录,您可以使用:
.SetFirstResult(2*25)
.SetMaxResults(25)
在尝试为此自己找到一个基于 NHibernate 的解决方案后,我最终只是在我碰巧使用的视图中添加了一个列:
CREATE VIEW vw_paged AS
SELECT ROW_NUMBER() OVER (ORDER BY Id) AS [Row], p.column1, p.column2
FROM paged_table p
如果您需要复杂的排序选项,这并没有真正的帮助,但它确实适用于简单的情况。
当然,条件查询看起来像这样:
public static IList<Paged> GetRange(string search, int rows)
{
var match = DbSession.Current.CreateCriteria<Job>()
.Add(Restrictions.Like("Id", search + '%'))
.AddOrder(Order.Asc("Id"))
.SetMaxResults(1)
.UniqueResult<Paged>();
if (match == null)
return new List<Paged>();
if (rows == 1)
return new List<Paged> {match};
return DbSession.Current.CreateCriteria<Paged>()
.Add(Restrictions.Like("Id", search + '%'))
.Add(Restrictions.Ge("Row", match.Row))
.AddOrder(Order.Asc("Id"))
.SetMaxResults(rows)
.List<Paged>();
}