ROW_NUMBER() and nhibernate - finding an item's page

前端 未结 3 889
旧巷少年郎
旧巷少年郎 2021-01-01 04:54

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

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-01 05:52

    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 from this answer.

    Here's the code:

    public abstract class RepositoryBase where T : DomainEntityBase
    {
        public long GetIndexOf(T entity, Expression> uniqueSelector, Expression> whereSelector, TWhere whereValue) where TWhere : DomainEntityBase
        {
            if (entity == null || entity.Id == Guid.Empty)
            {
                return -1;
            }
    
            var entityType = typeof(T).Name;
    
            var keyField = PropertyHelper.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.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();
    
            // The row_number() function is one-based. Our index should be zero-based.
            sqlRowNumber -= 1;
    
            return sqlRowNumber;
        }
    
        public long GetIndexOf(T entity, Expression> uniqueSelector)
        {
            return GetIndexOf(entity, uniqueSelector, null, (DomainEntityBase)null);
        }
    
        public long GetIndexOf(T entity, Expression> uniqueSelector, Expression> 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 { ... }
    
    ...
    
    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! :-)

提交回复
热议问题