2010-01-04 24 views
4

以ICriteria對象的形式給出查詢,我想使用NHibernate(通過投影的方式?)以等價的方式查找元素的順序 使用ROW_NUMBER()和nhibernate - 查找項目的頁面

SELECT ROW_NUMBER() OVER (...) 

在查詢中查找特定項目的索引。 (我需要這個在尋呼「跳轉到頁面」功能) 有什麼建議嗎?

注意:我不想去頁面,因爲它的編號尚未確定 - 我知道該怎麼做 - 我想獲取項目的INDEX,以便按頁面大小分頁並獲取頁面索引。

+0

這是我一直在尋找爲好。當你發佈這個問題時,我急切地等待所有的答案......在查看NHibernate的源代碼後,我確信沒有這樣的功能。 – 2010-01-13 10:34:59

+0

此外,row_number()函數的語法有點尷尬,可能會使其實現上述功能非常具有挑戰性。 – 2010-01-13 10:38:27

+0

你會考慮一個不基於ICriteria的解決方案,並且是SQL Server特有的嗎? (該解決方案不需要輸入魔法字符串,在功能上有所限制,但利用了您的域實體...) – 2010-01-15 16:51:43

回答

3

的ICriteria有這2個功能:

SetFirstResult() 

SetMaxResults() 

這將您的SQL語句轉換成MySql中使用ROW_NUMBER(在SQL Server)或限制。

所以,如果你想25個記錄的第三頁上,你可以使用:

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

謝謝,但我想要做的是有點不同 - 我有一個對象,我想找到它的索引,以便我可以找到對象的頁面。那麼我可以使用這些建議的方法來獲取該頁面。 – 2010-01-11 08:44:12

5

看源NHibernate的後,我相當肯定,不存在這樣的功能。

然而,我不介意有人證明我錯了。

在我的具體設置,我寫的是需要幾個lambda表達式的方法( - 特定域的實體的所有屬性代表鍵列,以及可選列通過篩選)解決了這個問題。這個方法然後建立sql並且調用session.CreateSQLQuery(...).UniqueResult();我並不是聲稱這是一個通用的解決方案。

爲了避免使用魔法字符串,我從this answer借了一份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); 

正如我所說的,這對我的作品。隨着我前進,我一定會調整它。因人而異。

現在,如果OP自己解決了這個問題,那麼我很樂意看到他的解決方案! :-)

+0

太棒了,如果我的投影計劃失敗,我可能會放棄它:) – 2010-01-25 15:26:56

0

試圖找到一個基於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>(); 
    }