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 Kendo UI MVC网格中添加“mailto:”链接
Telerik/ Kendo MVC Grid,按需加载网格,而不是页面加载