Telerik MVC Extensions Grid - How to have the grid filter apply to initial LINQ query or get passed down to db?

筅森魡賤 提交于 2019-12-24 02:09:46

问题


Currently in my MVC grids I am using normal Server Bindings, and the filters then are appended to the URL as query strings. The problem with this method is that if I am querying a grid that by default has thousands of records, but I am only displaying the first 30 records on the first page (paging filter) of my grid. Same thing would apply to a string filter for last name. I filter my 2000 records for last name smith, get 100 records, only 30 displayed on first page. I will then actually query a person object, have the full 2k objects returned, filter it to 100, and display 30. This is horribly inefficient.

How does one pass the filter parameters into a LINQ query for example so the initial query only returns results shown on that page? Also is there some automated way to do this generically for any grid? Or would you have to write this logic for every single grid you have?

I know if ToGridModel, which I use when exporting a grid to 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
    }

but I guess then another problem is that the grid itself is made up of ViewModels, not the POCO object. So even then, when I export to excel. I have to requery the whole result set, and then filter it down.

Surely there is a better way to do this?


回答1:


You can use Custom Binding to do this.

simple example of it you can read here: Telerik Documentation

For more generic approach you can use method CreateFilterExpression of class FilterDescriptor

Update

Generic example:

[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:


I prefer to use

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



回答3:


        if(request.Filters.Count > 0)
        {

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


来源:https://stackoverflow.com/questions/26593552/telerik-mvc-extensions-grid-how-to-have-the-grid-filter-apply-to-initial-linq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!