c# - 子集合 List<string>.Any 的表达式树

标签 c# linq lambda expression-trees

我正在使用表达式树构建通用 linq 查询。在子集合上创建表达式时我被卡住了。方法调用因类型不兼容而崩溃。通常我知道放在那里什么,但 Any() 方法调用让我感到困惑。我已经尝试了所有我能想到的类型,但没有运气。任何帮助,将不胜感激。

这是我的实体类:

public class Story : Entity
{
    public string Author { get; set; }

    public IList<string> Contributors { get; set; }
}

我要为其生成表达式树的查询:

var stories = new List<Story>();
stories.Where(p => p.Author.Contains("Test") || p.Contributors.Any(c => c.Contains("Test")));

到目前为止我得到了什么

public interface IFilterCriteria
{
    string PropertyToCompare { get; set; }
    object ValueToCompare { get; set; }
    FilterOperator FilterOperator { get; set; }
    bool IsList { get; set; }
    Expression Expression { get; set; }
}

public static IQueryable<T> Filter<T>(this IQueryable<T> query, IList<IFilterCriteria> filterCriterias, LogicalOperator logicalOperator = LogicalOperator.And)
{
    if (filterCriterias != null && filterCriterias.Any())
    {
        var resultCondition = filterCriterias.ToExpression(query, logicalOperator);

        var parameter = Expression.Parameter(query.ElementType, "p");

        if (resultCondition != null)
        {
            var lambda = Expression.Lambda(resultCondition, parameter);

            var mce = Expression.Call(
                typeof(Queryable), "Where",
                new[] { query.ElementType },
                query.Expression,
                lambda);

            return query.Provider.CreateQuery<T>(mce);
        }
    }
    return query;
}

public static Expression ToExpression<T>(this IList<IFilterCriteria> filterCriterias, IQueryable<T> query, LogicalOperator logicalOperator = LogicalOperator.And)
{
    Expression resultCondition = null;
    if (filterCriterias.Any())
    {
        var parameter = Expression.Parameter(query.ElementType, "p");

        foreach (var filterCriteria in filterCriterias)
        {
            var propertyExpression = filterCriteria.PropertyToCompare.Split('.').Aggregate<string, MemberExpression>(null, (current, property) => Expression.Property(current ?? (parameter as Expression), property));

            Expression valueExpression;
            var constantExpression = Expression.Constant(filterCriteria.ValueToCompare);

            if (!filterCriteria.IsList)
            {
                valueExpression = Expression.Convert(constantExpression, propertyExpression.Type);
            }
            else
            {
                valueExpression = Expression.Call(typeof (Enumerable), "Any", new[] {typeof (string)},
                                                  propertyExpression, filterCriteria.Expression,
                                                  Expression.Constant(filterCriteria.ValueToCompare,
                                                                      typeof (string)));
            }

            Expression condition;
            switch (filterCriteria.FilterOperator)
            {
                case FilterOperator.IsEqualTo:
                    condition = Expression.Equal(propertyExpression, valueExpression);
                    break;
                case FilterOperator.IsNotEqualTo:
                    condition = Expression.NotEqual(propertyExpression, valueExpression);
                    break;
                case FilterOperator.IsGreaterThan:
                    condition = Expression.GreaterThan(propertyExpression, valueExpression);
                    break;
                case FilterOperator.IsGreaterThanOrEqualTo:
                    condition = Expression.GreaterThanOrEqual(propertyExpression, valueExpression);
                    break;
                case FilterOperator.IsLessThan:
                    condition = Expression.LessThan(propertyExpression, valueExpression);
                    break;
                case FilterOperator.IsLessThanOrEqualTo:
                    condition = Expression.LessThanOrEqual(propertyExpression, valueExpression);
                    break;
                case FilterOperator.Contains:
                    condition = Expression.Call(propertyExpression, typeof(string).GetMethod("Contains", new[] { typeof(string) }), valueExpression);
                    break;
                case FilterOperator.StartsWith:
                    condition = Expression.Call(propertyExpression, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }), valueExpression);
                    break;
                case FilterOperator.EndsWith:
                    condition = Expression.Call(propertyExpression, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }), valueExpression);
                    break;
                default:
                    condition = valueExpression;
                    break;
            }

            if (resultCondition != null)
            {
                switch (logicalOperator)
                {
                    case LogicalOperator.And:
                        resultCondition = Expression.AndAlso(resultCondition, condition);
                        break;
                    case LogicalOperator.Or:
                        resultCondition = Expression.OrElse(resultCondition, condition);
                        break;
                }
            }
            else
            {
                resultCondition = condition;
            }
        }
    }
    return resultCondition;
}

这就是我使用表达式的方式:

var stories = new List<Story>();
var filters = new List<FilterCriteria>();
filter.Add(new FilterCriteria { ValueToCompare = "Test", PropertyToCompare = "Author", FilterOperator = FilterOperator.Contains });

Expression<Func<string, bool>> func  = t => t.Contains("Test");

filter.Add(new FilterCriteria { ValueToCompare = "Test", PropertyToCompare = "Contributors", FilterOperator = FilterOperator.Contains, Expression = func });

stories.Filter(filters, LogicalOperator.Or).ToList();

但是在运行这段代码之后,我得到了这个我无法解决的错误

No generic method 'Any' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: No generic method 'Any' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

Source Error:

Line 184: { Line 185:
var overload = typeof(Queryable).GetMethods().Single(mi => mi.Name == "Any" && mi.GetParameters().Count() == 2); Line 186:
Expression.Call(typeof(Queryable), "Any", new[] { typeof(string) }, propertyExpression, or); Line 187:
valueExpression = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(string)}, propertyExpression, or, Expression.Constant("Test",

最佳答案

您不要在代码中的任何地方调用 Any 方法。

你应该扩展Contains,例如:

case FilterOperator.Contains:
    // if collection
    if (propertyExpression.Type.IsGenericType &&
        typeof(IEnumerable<>)
            .MakeGenericType(propertyExpression.Type.GetGenericArguments())
            .IsAssignableFrom(propertyExpression.Type))
    {
        // find AsQueryable method
        var toQueryable = typeof(Queryable).GetMethods()
            .Where(m => m.Name == "AsQueryable")
            .Single(m => m.IsGenericMethod)
            .MakeGenericMethod(typeof(string));

        // find Any method
        var method = typeof(Queryable).GetMethods()
            .Where(m => m.Name == "Any")
            .Single(m => m.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(string));

        // make expression
        condition = Expression.Call(
            null, 
            method,
            Expression.Call(null, toQueryable, propertyExpression), 
            filterCriteria.Expression
        );
    }
    else
    {
        condition = Expression.Call(propertyExpression, typeof(string).GetMethod("Contains", new[] { typeof(string) }), valueExpression);
    }
    break;

你还应该创建一个 p 参数 (Expression.Parameter(query.ElementType, "p")) 否则你会得到 variable 'p ' 类型 'WpfApplication2.Story' 从范围 '' 引用,但未定义 错误。

您可以将 parameterFilter 方法传递给 ToExpression 方法。

关于c# - 子集合 List<string>.Any 的表达式树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19748478/

相关文章:

c# - 如何测量函数运行了多长时间?

c# - 在循环中使用 FindControl 获取以编程方式添加的文本框的值

c# - 为什么这个 LINQ 查询可以编译?

c# - LINQ 中哪种方法更好?

linq - 如何删除 linq to xml 文档中的重复节点

java流过滤器不显示输出

c# - 如何在 asp.net mvc 中使用 DataAnnotations 验证字符串长度?

c# - iisexpress : listen on external interface without modifying config file:

c# - 使用 lambda 表达式左连接多个表

c# - 比较 : LINQ vs LAMBDA Expression