1

设想:

我有一个 JavaScript 库,它使用服务器端挂钩处理表的分页/排序/搜索。我已经连接了服务器端 JSON 调用,创建了一个模型绑定器,将传入的值转换为可用的请求对象,现在我正在尝试实现搜索。

通过请求对象,我可以访问要排序的列列表。我正在尝试编写一个通用函数,该函数将允许任何表传入其列,并为每列和搜索值生成 .Where 调用。

目前的方法是获取字符串属性名称,使用反射获取实际值,然后将其与搜索参数进行比较。我正在尝试的非常粗略的方法是:

public static IQueryable GetSearchClause(IQueryable<object> query, DataTablesPageRequest pageRequest)
{
    var columns = pageRequest.ColumnNames.Split(',');

    for (var i = 0; i < pageRequest.Searchable.Count; ++i)
    {
        if (pageRequest.Searchable[i])
        {
            var column = columns[i];

            var test = query.Where(x => x.GetType().GetProperty(column)
                .GetValue(query.Select(z => z.GetType()), null)
                .Equals("test"));
        }
    }

    return query;
}

当我查看生成的“测试”对象时,我得到了一个非常不愉快的堆栈跟踪:

   at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression(MethodCallExpression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.Visit(Expression expression, VisitorParameters parameters)
   at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
   at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
   at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
   at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
   at NHibernate.Linq.Visitors.QueryModelVisitor.Visit()
   at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root)
   at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory)
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(String queryIdentifier, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.HQLExpressionQueryPlan.CreateTranslators(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
   at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
   at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
   at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

我什至可以用这种技术完成我想要做的事情,还是我真的应该使用逐实体方法,因为我会提前知道要搜索哪些列(并跳过整个反射位)?

4

1 回答 1

1

任务完成!在我的所有存储库继承的基础存储库中创建了这个,目前它足够通用,可以跨多个实体工作,但我相信我将来会遇到问题。这里唯一真正有趣的是我希望子列被命名为“parent.child”,这是我在 DataTables 列命名中强制执行的约定。

public ICriteriaWrapper GetWithPagingSortingFiltering(
    Type entityType,
    string columnNames, 
    List<bool> searchableColumns, 
    string searchParameter,
    int numSortingColumns,
    List<int> sortingColumns,
    List<string> sortingDirection, 
    int currentPage,
    int itemsPerPage)
{
    // Create the criteria for the given entity type
    var crit = _session.CreateCriteria(entityType);

    // Split the columns, which will be used as our properties
    var columns = columnNames.Split(',');

    // Add criteria for searchable columns, so long as a parameter is given
    if (searchParameter != string.Empty)
    {
        var disjunction = Restrictions.Disjunction();

        for (var i = 0; i < searchableColumns.Count; ++i)
        {
            if (searchableColumns[i])
            {
                var column = columns[i];
                var columnParts = column.Split('.');

                // Handles immediate children only
                if (columnParts.Count() == 2)
                {
                    var child = columnParts[0];
                    var aliasName = "the" + child;
                    var propertyName = aliasName + "." + columnParts[1];

                    crit.CreateAlias(child, aliasName);

                    disjunction.Add(
                        Restrictions.Like(
                            Projections.Cast(NHibernateUtil.AnsiString, Projections.Property(propertyName)),
                            searchParameter, MatchMode.Start));
                }
                // Handles base level properties
                else if (columnParts.Count() == 1)
                {
                    disjunction.Add(
                        Restrictions.Like(
                            Projections.Cast(NHibernateUtil.AnsiString, Projections.Property(column)),
                            searchParameter, MatchMode.Start));
                }
                else
                {
                    throw new Exception("Unrecognized number of children; add more conditionals!");
                }
            }
        }

        crit.Add(disjunction);
    }

    // Grab the total items count
    var totalItemsCrit = CriteriaTransformer.Clone(crit);
    var totalItems = totalItemsCrit.SetProjection(Projections.RowCount()).UniqueResult();

    // Apply ordering
    for (var i = 0; i < numSortingColumns; ++i)
    {
        var direction = sortingDirection[i];
        var column = columns[sortingColumns[i]];

        if (direction == "asc")
            crit.AddOrder(Order.Asc(column));
        else if (direction == "desc")
            crit.AddOrder(Order.Desc(column));   
    }

    // Apply paging
    var startPage = (itemsPerPage == 0
                         ? 1
                         : currentPage/itemsPerPage + 1);

    crit.SetFirstResult(startPage).SetMaxResults(itemsPerPage);

    return new ICriteriaWrapper
        {
            Criteria = crit,
            TotalItems = Convert.ToInt32(totalItems)
        };
}
于 2012-08-06T20:51:27.703 回答