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
我正在尝试构建一个动态 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