C# 在 Linq-to-Entities 中构建表达式,搜索类型中所有属性的字符串表示

C# Building Expressions in Linq-to-Entities, Searching all properties' string representations in a type

我正在尝试构建一个动态 LINQ-to-Entities 查询生成器,以 return 使用 属性 名称作为字符串或检查类型中所有属性的表达式来过滤表达式。如果 属性 是一个整数,我想检查它是否等于搜索值,否则我想看看 属性 的字符串值是否包含搜索值。

构建器有两种方法:一种是在将 属性 名称作为字符串和搜索值传递给 lambda 表达式时 return,另一种是根据搜索值。

为了针对单个 属性 进行搜索,我有以下内容:

    public static Expression<Func<TEntity, bool>> GetFilter<TEntity>(string data, string propertyName)
    {
        var parameter = Expression.Parameter(typeof(TEntity));
        Type t = typeof(TEntity).GetProperty(propertyName).PropertyType;
        var property = Expression.PropertyOrField(parameter, propertyName);
        ConstantExpression constant;
        Expression predicate;



        if (t == typeof(int))
        {
            int temp;
            bool check = int.TryParse(data, out temp);

            if (!check)
            {
                return ((TEntity x) => false);
            }

            constant = Expression.Constant(temp);


            predicate = Expression.Call(property, "Equals", null, constant);
        }
        else
        {
            constant = Expression.Constant(data);
            var toString = Expression.Call(property, "ToString", null);

            predicate = Expression.Call(toString, "Contains", null, constant);



        }


        return Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

    }

这非常有效。但是,当尝试对所有属性执行此操作时,我收到 NotSupportedException,指出 ToString() 无法转换为有效的 SQL 查询。

为了设置表达式,我有一个临时的表达式叫exp,设置它的代码完全一样。然后我通过调用 Expression.Or()

'concatenate' 表达式
    public static Expression<Func<TEntity, bool>> GetFilterForAll<TEntity>(string data)
    {

        Type t = typeof(TEntity);
        var parameter = Expression.Parameter(t);

        var properties = from p in t.GetProperties()
                         where p.CanRead && !p.GetGetMethod().IsVirtual && p.Name.ToLower() != "id"
                         select Expression.PropertyOrField(parameter, p.Name);


        Expression predicate = null;

        foreach (var prop in properties)
        {
            ConstantExpression constant;


            Expression exp;

            if (prop.Type == typeof(int))
            {
                int temp;
                bool check = int.TryParse(data, out temp);

                if (!check)
                {
                    continue;
                }

                constant = Expression.Constant(temp);


                exp = Expression.Call(prop, "Equals", null, constant);
            }
            else
            {
                constant = Expression.Constant(data);
                var toString = Expression.Call(prop, "ToString", null);

                exp = Expression.Call(toString, "Contains", null, constant);


            }



            if (predicate == null)
            {
                predicate = exp;
            }
            else
            {
                predicate = Expression.Or(predicate, exp);
            }

        }

        if (predicate == null) return null;

        return Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);

    }

我知道我在某处出错了,尤其是今天已经学到了这一切,我怀疑这就是我做 'Or' 表达式的方式。

我在没有 Equals 的情况下执行此操作,因为不在范围内,我需要创建一些属性或标志来设置道具将被过滤 Exactly or Likely, 我只是进入导航的深度 1 级,也许这可以帮助您理解:

private static readonly MethodInfo ToStringMethod = typeof(object).GetMethod("ToString");
private static readonly MethodInfo StringContainsMethod = typeof(string).GetMethod("Contains");

public static Expression<Func<T, bool>> BuildFilterPredicate<T>(string q)
{
    var query = Expression.Constant(q);
    var type = typeof(T);
    var lambdaParam = Expression.Parameter(type);
    var predicates = type.GetProperties().SelectMany(p => PredicateContainsBuilder(lambdaParam, p, query)).ToList();
    Expression body = predicates[0];
    body = predicates.Skip(1).Aggregate(body, Expression.OrElse);
    return Expression.Lambda<Func<T, bool>>(body, lambdaParam);
}

private static IEnumerable<MethodCallExpression> PredicateContainsBuilder(Expression lambdaParam, PropertyInfo prop, Expression query)
{

    if (prop.PropertyType.IsClass)
        return new List<MethodCallExpression> { Expression.Call(Expression.Call(Expression.Property(lambdaParam, prop), ToStringMethod), StringContainsMethod, query) };

    var properties = prop.PropertyType.GetProperties();
    return properties.Select(p => Expression.Call(Expression.Call(Expression.Property(lambdaParam, p), ToStringMethod), StringContainsMethod, query)).ToList();
}

尝试使用 OrElse 而不是 Or