从Kendo UI网格中创建具有相关对象的表达式[FilterDescriptor]

rse*_*nem 2 lambda expression kendo-asp.net-mvc kendo-ui-mvc

我正在尝试为FilterDescriptor构建一个表达式生成器,该表达式生成器是我从一个请求中获取的,以便在DB中执行,因为它仅在内存中执行。我有成千上万的行可以从数据库中检索(这就是为什么我要创建一个构建器以减少负载的原因)。

//classes
public class Product
{
   public string Name {get;set;}
   public TypeOfProduct {get;set;}
}
public class TypeOfProduct
{
   public string Description {get;set;}
}

//action Read
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
    if (request.Filters.Count > 0)
    {
        where = ApplyFilter(request.Filters[0]);
    }
}

// apply the FilterDescriptor to a string 
private static string ApplyFilter(IFilterDescriptor filter)
{
    var filters = string.Empty;
    if (filter is CompositeFilterDescriptor)
    {
        filters += "(";
        var compositeFilterDescriptor = (CompositeFilterDescriptor)filter;
        foreach (IFilterDescriptor childFilter in compositeFilterDescriptor.FilterDescriptors)
        {
            filters += ApplyFilter(childFilter);
            filters += string.Format(" {0} ", compositeFilterDescriptor.LogicalOperator.ToString());
        }
    }
    else
    {
        string filterDescriptor = "{0} {1} {2}";
        var descriptor = (FilterDescriptor)filter;
        if (descriptor.Operator == FilterOperator.IsEqualTo)
        {
            System.Linq.Expressions.Expression<Func<Volume, bool>> func = ExpressionExtension.ConvertStringTo<Volume>(member, "=", descriptor.Value.ToString());
        }

        filters = filterDescriptor;
    }

    filters = filters.EndsWith("And ") == true ? string.Format("{0})", filters.Substring(0, filters.Length - 4)) : filters;
    filters = filters.EndsWith("Or ") == true ? string.Format("{0})", filters.Substring(0, filters.Length - 4)) : filters;

    return filters;
}


// Expression Generator
public static class ExpressionExtension
    {
        public static Expression<Func<T, bool>> ConvertStringTo<T>(string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
        {
            Expression<Func<T, bool>> func = null;
            try
            {
                PropertyInfo prop = null;
                foreach (var x in propName.Split('.'))
                {
                    if (prop == null)
                    {
                        prop = typeof(T).GetProperty(x);
                    }
                    else
                    {
                        prop = prop.PropertyType.GetProperty(x);
                    }
                }
                ParameterExpression tpe = Expression.Parameter(typeof(T));
                Expression left = Expression.Property(tpe, prop);
                Expression right = Expression.Convert(To(prop, value), prop.PropertyType);
                Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(ApplyFilter(opr, left, right), tpe);
                if (expr != null)
                {
                    innerExpr = innerExpr.And(expr);
                }
                func = innerExpr;
            }
            catch
            {
            }
            return func;
        }

        public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Func<T, TResult> ExpressionToFunc<T, TResult>(this Expression<Func<T, TResult>> expr)
        {
            return expr.Compile();
        }

        private static Expression To(PropertyInfo prop, string value)
        {
            object val = Convert.ChangeType(value, ResolveType(prop.PropertyType.ToString()));

            return Expression.Constant(val);
        }

        private static Type ResolveType(String typeName)
        {
            Type type = Type.GetType(typeName);
            if (type == null)
            {
                return null;
            }

            Type underlying = Nullable.GetUnderlyingType(type);

            if (underlying != null)
            {
                type = underlying;
            }
            return type;
        }


        private static BinaryExpression ApplyFilter(string opr, Expression left, Expression right)
        {
            BinaryExpression innerLambda = null;
            switch (opr)
            {
                case "=":
                    innerLambda = Expression.Equal(left, right);
                    break;
            }
            return innerLambda;
        }
    }
Run Code Online (Sandbox Code Playgroud)

问题:如果我的过滤器带有两个字段(“名称”和“描述”),那么如何为两个字段创建联接表达式,因为我需要使用Product创建到TypeOfProduct的联接?

已编辑

return this.Json(this.FactoryDomain.Get().ToDataSourceResult(request, volume => new
            {
                volume.Id,
                volume.ClasseProcessoMarcacao.ClienteId,
                volume.ClasseProcessoMarcacao.Cliente.Pessoa.NomeReduzido,
                volume.ClasseProcessoMarcacao.NomenclaturaPadrao,
                volume.ControleProducaoIdAtual,
                volume.CodigoBarras,
                volume.NumeroCliente,
                volume.NumeroGeral
            }));
Run Code Online (Sandbox Code Playgroud)

这是一个示例,其中我将数据返回到网格,但是如果我过滤“ NomeReduzido”属性,则会引发错误。我决定在查询中创建过滤器以直接在DB中执行。

Sta*_*zer 5

这是一个很晚的答案,但对于其他用户可能仍然派上用场。您是否尝试过使用Kendo的Expression Builder来完成此任务,如下所示:

.Where(Kendo.Mvc.ExpressionBuilder.Expression<T>(request.Filters, false).Compile())
Run Code Online (Sandbox Code Playgroud)

我将它与Entity Framework和/或Telerik的Data Access结合使用以满足我的过滤需求。

  • 这对我来说失败了,并出现“NotSupportedException”“LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke””。如果您省略“Compile”调用,尽管您只得到一个表达式树,它在 LINQ“Where”子句中工作得很好! (2认同)