如何在Entity Framework中动态构造Where表达式?
How to construct Where Expression dynamically in Entity Framework?
我查看了 如何在 Entity Framework 中动态创建 OrderBy 表达式。但我还想构建一个动态的 Where 表达式。类似这样的事情:
public IEnumerable<InventoryItem> GetAll(string filterBy, object value)
{
var results = new List<InventoryItem>();
using (var db = new InventoryDb())
{
if (QueryHelper.PropertyExists<InventoryItem>(filterBy))
{
var query = db.rri_InventoryItems.WhereByProperty(filterBy, value);
foreach(var item in query.Where(expr))
{
results.Add(ConvertItem(item));
}
}
}
return results;
}
传入 属性 进行过滤,并将值作为 ab 对象。 Queryable 有两种 Where 方法,它们都采用两个参数,所以我什至不确定哪个是正确的。
此时我更加迷茫了。我不确定如何重构原始 OrderByProerty 方法以提供 WhereByProperty。我知道我这里的东西是完全错误的。我不知道该怎么办。
理想情况下,我想通过提供一组对象来进一步扩展它,这些对象可用于使用 ands 和 or 运算符。
Queryable has two methods for Where that both take two parameters, so I am not even sure which is the proper one.
您需要接收 Expression<Func<T, bool>> predicate
的那个。
以下是动态构建类似于 (T item) => item.Property == value
的谓词的方法:
public static partial class QueryableExtensions
{
public static IQueryable<T> WhereEquals<T>(this IQueryable<T> source, string member, object value)
{
var item = Expression.Parameter(typeof(T), "item");
var memberValue = member.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
var memberType = memberValue.Type;
if (value != null && value.GetType() != memberType)
value = Convert.ChangeType(value, memberType);
var condition = Expression.Equal(memberValue, Expression.Constant(value, memberType));
var predicate = Expression.Lambda<Func<T, bool>>(condition, item);
return source.Where(predicate);
}
}
我尝试以这种方式编写它,以便您可以跳过代码以了解它的作用。唯一可能需要解释的行是:
var memberValue = member.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
这是处理 obj.Prop1.Prop2
等嵌套属性的简单方法。如果您不需要这种功能,您可以简单地使用它:
var memberValue = Expression.PropertyOrField(item, member);
我还不需要嵌套属性。我稍微修改了您的代码并使它正常工作:
public static IQueryable<T> WhereEquals<T>(
this IQueryable<T> source, string propertyName, object value)
{
if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression parameter = Expression.Parameter(typeof(T), "item");
Expression whereProperty = Expression.Property(parameter, propertyName);
Expression constant = Expression.Constant(value);
Expression condition = Expression.Equal(whereProperty, constant);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(condition,parameter);
return source.Where(lambda);
}
我查看了
public IEnumerable<InventoryItem> GetAll(string filterBy, object value)
{
var results = new List<InventoryItem>();
using (var db = new InventoryDb())
{
if (QueryHelper.PropertyExists<InventoryItem>(filterBy))
{
var query = db.rri_InventoryItems.WhereByProperty(filterBy, value);
foreach(var item in query.Where(expr))
{
results.Add(ConvertItem(item));
}
}
}
return results;
}
传入 属性 进行过滤,并将值作为 ab 对象。 Queryable 有两种 Where 方法,它们都采用两个参数,所以我什至不确定哪个是正确的。
此时我更加迷茫了。我不确定如何重构原始 OrderByProerty 方法以提供 WhereByProperty。我知道我这里的东西是完全错误的。我不知道该怎么办。
理想情况下,我想通过提供一组对象来进一步扩展它,这些对象可用于使用 ands 和 or 运算符。
Queryable has two methods for Where that both take two parameters, so I am not even sure which is the proper one.
您需要接收 Expression<Func<T, bool>> predicate
的那个。
以下是动态构建类似于 (T item) => item.Property == value
的谓词的方法:
public static partial class QueryableExtensions
{
public static IQueryable<T> WhereEquals<T>(this IQueryable<T> source, string member, object value)
{
var item = Expression.Parameter(typeof(T), "item");
var memberValue = member.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
var memberType = memberValue.Type;
if (value != null && value.GetType() != memberType)
value = Convert.ChangeType(value, memberType);
var condition = Expression.Equal(memberValue, Expression.Constant(value, memberType));
var predicate = Expression.Lambda<Func<T, bool>>(condition, item);
return source.Where(predicate);
}
}
我尝试以这种方式编写它,以便您可以跳过代码以了解它的作用。唯一可能需要解释的行是:
var memberValue = member.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
这是处理 obj.Prop1.Prop2
等嵌套属性的简单方法。如果您不需要这种功能,您可以简单地使用它:
var memberValue = Expression.PropertyOrField(item, member);
我还不需要嵌套属性。我稍微修改了您的代码并使它正常工作:
public static IQueryable<T> WhereEquals<T>(
this IQueryable<T> source, string propertyName, object value)
{
if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression parameter = Expression.Parameter(typeof(T), "item");
Expression whereProperty = Expression.Property(parameter, propertyName);
Expression constant = Expression.Constant(value);
Expression condition = Expression.Equal(whereProperty, constant);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(condition,parameter);
return source.Where(lambda);
}