ROW_NUMBER() 和 nhibernate - 查找项目的页面

发布于 2024-08-16 17:03:08 字数 252 浏览 10 评论 0原文

给定一个 ICriteria 对象形式的查询,我想使用 NHibernate (通过投影?)来查找元素的顺序, 其方式相当于使用

SELECT ROW_NUMBER() OVER (...)

在查询中查找特定项目的索引。 (我需要这个来实现分页中的“跳转到页面”功能) 有什么建议吗?

注意:我不想转到给定编号的页面 - 我知道该怎么做 - 我想获取该项目的索引,以便我可以将其除以页面大小并获取页面索引。

given a query in the form of an ICriteria object, I would like to use NHibernate (by means of a projection?) to find an element's order,
in a manner equivalent to using

SELECT ROW_NUMBER() OVER (...)

to find a specific item's index in the query.
(I need this for a "jump to page" functionality in paging)
any suggestions?

NOTE: I don't want to go to a page given it's number yet - I know how to do that - I want to get the item's INDEX so I can divide it by page size and get the page index.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

遥远的绿洲 2024-08-23 17:03:09

在查看了 NHibernate 的源代码之后,我相当确定不存在这样的功能。

但是,我不介意有人证明我错了。

在我的特定设置中,我确实通过编写一个需要几个 lambda 的方法来解决这个问题(表示键列,以及用于过滤的可选列 - 特定域实体的所有属性)。然后,此方法构建 sql 并调用 session.CreateSQLQuery(...).UniqueResult(); 我并不是说这是一个通用解决方案。

为了避免使用魔术字符串,我从 这个答案

这是代码:

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自己解决了这个问题,那么我很想看看他的解决方案! :-)

After looking at the sources for NHibernate, I'm fairly sure that there exists no such functionality.

I wouldn't mind, however, for someone to prove me wrong.

In my specific setting, I did solve this problem by writing a method that takes a couple of lambdas (representing the key column, and an optional column to filter by - all properties of a specific domain entity). This method then builds the sql and calls session.CreateSQLQuery(...).UniqueResult(); I'm not claiming that this is a general purpose solution.

To avoid the use of magic strings, I borrowed a copy of PropertyHelper<T> from this answer.

Here's the code:

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; }
}

And you use it like so:

...

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);

As I said, this works for me. I'll definitely tweak it as I move forward. YMMV.

Now, if the OP has solved this himself, then I would love to see his solution! :-)

夏日落 2024-08-23 17:03:09

ICriteria 有这 2 个功能:

 SetFirstResult()

 SetMaxResults()

SQL 语句转换为使用 ROW_NUMBER (在 sql server 中)或在 MySql 中限制。

因此,如果您想在第三页上有 25 条记录,您可以使用:

 .SetFirstResult(2*25) 
 .SetMaxResults(25)

ICriteria has this 2 functions:

 SetFirstResult()

and

 SetMaxResults()

which transform your SQL statement into using ROW_NUMBER (in sql server) or limit in MySql.

So if you want 25 records on the third page you could use:

 .SetFirstResult(2*25) 
 .SetMaxResults(25)
墨小沫ゞ 2024-08-23 17:03:09

在尝试自己找到基于 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>();
    }

After trying to find an NHibernate based solution for this myself, I ultimately just added a column to the view I happened to be using:

CREATE VIEW vw_paged AS
SELECT ROW_NUMBER() OVER (ORDER BY Id) AS [Row], p.column1, p.column2
FROM paged_table p

This doesn't really help if you need complex sorting options, but it does work for simple cases.

A Criteria query, of course, would look something like this:

    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>();
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文