【问题标题】:Expression tree for child collection List<string>.Any子集合 List<string>.Any 的表达式树
【发布时间】:2013-11-04 20:57:28
【问题描述】:

我正在使用表达式树构建通用 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();

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

“System.Linq.Queryable”类型上的通用方法“Any”不兼容 使用提供的类型参数和参数。没有类型参数 如果方法是非泛型的,则应提供。说明:一个 当前 web 执行过程中发生未处理的异常 要求。请查看堆栈跟踪以获取有关 错误以及它起源于代码的位置。

异常详细信息:System.InvalidOperationException:没有通用方法 'System.Linq.Queryable' 类型上的 'Any' 与提供的兼容 类型参数和参数。如果出现以下情况,则不应提供类型参数 该方法是非通用的。

来源错误:

第 184 行:{ 第 185 行:
var 重载 = typeof(Queryable).GetMethods().Single(mi => mi.Name == “任何” && mi.GetParameters().Count() == 2);第 186 行:
Expression.Call(typeof(Queryable), "Any", new[] { typeof(string) }, 属性表达式,或);第 187 行:
valueExpression = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(string)}, propertyExpression, or, Expression.Constant("Test",

【问题讨论】:

    标签: c# linq lambda expression-trees


    【解决方案1】:

    您不会在代码中的任何位置调用 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' of type 'WpfApplication2.Story' referenced from scope '', but it is not defined 错误。

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

    【讨论】:

    • 谢谢Lolo,它按我的预期工作,请您推荐任何好的书籍或参考资料以进一步阅读Expression Tree。
    • @mgarg 不是真的,我没有读过关于表达式的任何内容,我想说我是自学成才的。这个网站是我的第一个信息来源:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    相关资源
    最近更新 更多