Telerik MVC Extensions Grid - 如何让网格过滤器应用于初始 LINQ 查询或传递给 db?

Posted

技术标签:

【中文标题】Telerik MVC Extensions Grid - 如何让网格过滤器应用于初始 LINQ 查询或传递给 db?【英文标题】:Telerik MVC Extensions Grid - How to have the grid filter apply to initial LINQ query or get passed down to db? 【发布时间】:2014-12-22 23:38:43 【问题描述】:

目前在我的 MVC 网格中,我使用的是普通的服务器绑定,然后过滤器作为查询字符串附加到 URL。这种方法的问题是,如果我查询一个默认有数千条记录的网格,但我只在我的网格的第一页(分页过滤器)上显示前 30 条记录。同样的事情也适用于姓氏的字符串过滤器。我过滤了姓 smith 的 2000 条记录,得到 100 条记录,只有 30 条显示在第一页。然后我将实际查询一个人对象,返回完整的 2k 个对象,将其过滤为 100,然后显示 30。这非常低效。

例如,如何将过滤器参数传递到 LINQ 查询中,以便初始查询仅返回该页面上显示的结果?还有一些自动化的方法可以为任何网格通用地执行此操作吗?或者您是否必须为您拥有的每个网格编写此逻辑?

我知道ToGridModel,我在将网格导出到excel时使用它:

 public ActionResult Export(int page, string orderBy, string filter, string groupBy)
    
        //Get the data representing the current grid state - page, sort and filter
        List<Building> buildings = _buildingService.GetList();
        List<BuildingModel> buildingModels = new List<BuildingModel>();

        buildings.ForEach(x => buildingModels.Add(_buildingService.ConvertToModel(x)));

        GridModel model = buildingModels.AsQueryable().ToGridModel(1, buildingModels.Count, orderBy, groupBy, filter);

        MemoryStream fileOutput = ExcelUtil.ExportGridModelToExcel<BuildingModel>(model);

        return File(fileOutput.ToArray(),   //The binary data of the XLS file
            "application/vnd.ms-excel", //MIME type of Excel files
            "BuildingsReport.xls");     //Suggested file name in the "Save as" dialog which will be displayed to the end user
    

但我想另一个问题是网格本身是由 ViewModel 组成的,而不是 POCO 对象。所以即使那样,当我导出到excel时。我必须重新查询整个结果集,然后将其过滤掉。

肯定有更好的方法来做到这一点?

【问题讨论】:

【参考方案1】:

您可以使用自定义绑定来执行此操作。

您可以在这里阅读它的简单示例:Telerik Documentation

对于更通用的方法,您可以使用类 FilterDescriptor 的方法 CreateFilterExpression

更新

通用示例:

[GridAction(EnableCustomBinding = true)]
public ViewResult GetRecords(GridCommand command)

    using (var context = _contextFactory())
    
        var records = context.Set<Records>();
        if (command.FilterDescriptors.Any())    //RequestNumber
                            
            var filter = command.FilterDescriptors.GetFilter<ChangeRecord>();
            records = records.Where(filter);
        
        return View(new GridModel(records.ToArray()));
    


public static class GridCommandExtensions

    public static Expression<Func<TGridModel, bool>> GetFilter<TGridModel>(this IList<IFilterDescriptor> filterDescriptors)
    
        var filters = filterDescriptors.SelectMany(GetAllFilterDescriptors).ToArray();
        var parameter = Expression.Parameter(typeof(TGridModel), "c");
        if (filters.Length == 1)
            return Expression.Lambda<Func<TGridModel, bool>>(GetExpression(parameter, filters[0]), parameter);

        Expression exp = null;
        for (int index = 0; index < filters.Length; index += 2)   // условие И
        
            var filter1 = filters[index];

            if (index == filters.Length - 1)
            
                exp = Expression.AndAlso(exp, GetExpression(parameter, filter1));
                break;
            
            var filter2 = filters[index + 1];
            var left = GetExpression(parameter, filter1);
            var right = GetExpression(parameter, filter2);
            exp = exp == null
                ? Expression.AndAlso(left, right)
                : Expression.AndAlso(exp, Expression.AndAlso(left, right));
        

        return Expression.Lambda<Func<TGridModel, bool>>(exp, parameter);
    
    private static Expression GetExpression(ParameterExpression parameter, FilterDescriptor filter)
    
        var containsMethod = typeof(string).GetMethod("Contains", new[]  typeof(string) );
        var startsWithMethod = typeof(string).GetMethod("StartsWith", new[]  typeof(string) );
        var endsWithMethod = typeof(string).GetMethod("EndsWith", new[]  typeof(string) );

        var property = filter.Member.Contains(".") ?
            filter.Member.Split('.').Aggregate((Expression)parameter, Expression.Property)  // (x => x.Property.FieldName)
            : Expression.Property(parameter, filter.Member);                                // (x => x.FieldName)
        var constant = Expression.Constant(filter.Value);               // значение для выражения

        switch (filter.Operator)
        
            case FilterOperator.IsEqualTo:
                return Expression.Equal(property, constant);
            case FilterOperator.IsNotEqualTo:
                return Expression.NotEqual(property, constant);

            case FilterOperator.Contains:
                return Expression.Call(property, containsMethod, constant);
            case FilterOperator.StartsWith:
                return Expression.Call(property, startsWithMethod, constant);
            case FilterOperator.EndsWith:
                return Expression.Call(property, endsWithMethod, constant);

            case FilterOperator.IsGreaterThan:
                return Expression.GreaterThan(property, constant);
            case FilterOperator.IsGreaterThanOrEqualTo:
                return Expression.GreaterThanOrEqual(property, constant);
            case FilterOperator.IsLessThan:
                return Expression.LessThan(property, constant);
            case FilterOperator.IsLessThanOrEqualTo:
                return Expression.LessThanOrEqual(property, constant);
            default:
                throw new InvalidOperationException(string.Format("Неподдерживаемая операция 0 для колонки 1", filter.Operator, filter.Member));
        
    
    public static IEnumerable<FilterDescriptor> GetAllFilterDescriptors(this IFilterDescriptor descriptor)
    
        var filterDescriptor = descriptor as FilterDescriptor;
        if (filterDescriptor != null)
        
            yield return filterDescriptor;
            yield break;
        

        var compositeFilterDescriptor = descriptor as CompositeFilterDescriptor;
        if (compositeFilterDescriptor != null)
        
            if (compositeFilterDescriptor.LogicalOperator == FilterCompositionLogicalOperator.Or)
                throw new ArgumentOutOfRangeException("descriptor", "В фильтрах не поддерживается OR");

            foreach (var childDescriptor in compositeFilterDescriptor.FilterDescriptors.SelectMany(GetAllFilterDescriptors))
                yield return childDescriptor;
        
    
 

【讨论】:

谢谢 看起来像我需要的,你有通用方法的工作示例吗?该链接似乎不包含该内容。 如果你展示你的绑定方法的例子,我会试着写它。我自己的通用示例适用于数据集。 我目前正在使用服务器绑定,但获取数据与导出方法中的第一行没有什么不同。获取建筑物列表,转换为模型,这是网格使用的数据。 这正是我所需要的!谢谢!【参考方案2】:
        if(request.Filters.Count > 0)
        

            foreach (Kendo.Mvc.FilterDescriptor f in request.Filters)
            
                f.Value = f.Value.ToString().Trim();
            
        

【讨论】:

请考虑为您的代码添加解释。仅代码的答案通常被认为是低质量的。【参考方案3】:

我更喜欢使用

IEnumerable<Building> buildings = _buildingService.GetIEnumerable().AsQueryable().ToGridModel(page, pageSize, orderBy, string.Empty, filter).Data.Cast<Building>();

【讨论】:

这行不通,因为我使用的不是建筑,而是建筑模型。我的视图模型与 EF 构建类不是一对一的。

以上是关于Telerik MVC Extensions Grid - 如何让网格过滤器应用于初始 LINQ 查询或传递给 db?的主要内容,如果未能解决你的问题,请参考以下文章

Telerik MVC Grid Ajax 手动绑定

在Telerik Kendo UI MVC网格中添加“mailto:”链接

带有大型数据库的 Mvc Telerik 网格

Telerik/ Kendo MVC Grid,按需加载网格,而不是页面加载

Telerik Kendo Grid (MVC) 更新后刷新

Telerik Kendo MVC 文本框多行模式