查询的通用方法 Entity Framework
Generic method for querying Entity Framework
我想要通过 IEnumerable<TEnum>
过滤 Entity Framework IQueryable<TItem>
的通用方法。它的签名大概应该是这样的:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TItem, IEnumerable<TEnum>, bool>> predicate)
{
return items.Where(??????);
}
我希望能够像这样调用它:
IQueryable<Request> requests = service.GetAllRequests();
IEnumerable<RequestState> states = new RequestState[] {RequestState.Active, RequestState.Closed};
Expression<Func<Request, IEnumerable<RequestState>, bool>> predicate = (r, s) => s.Contains(r.State);
requests = ApplyFilter(requests, states, predicate);
但是方法体内应该有什么?如何将 Expression<Func<TItem, IEnumerable<TEnum>, bool>>
转换为 Expression<Func<TItem, bool>>
以用作 "Where" 方法的参数?它可以与 Entity Framework 一起使用吗?
IMO,谓词应该在你的 ApplyFilter
方法中(考虑到问题)。
一种可能的编码方式是:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items,
IEnumerable<TEnum> enumValues, Expression<Func<TItem,TEnum>> enumField)
{
return items.Where(i => enumValues.Contains(enumField(i)));
}
这样的电话:
requests = ApplyFilter(requests, states, r => r.State);
当我在实施 Sharped 的回答时,我发现实际上可以完全按照我的意愿去做。重点是使用继承自 ExpressionVisitor 的自定义 class。此 class 将用其实际值替换表达式树中每个出现的 IEnumerable<TEnum>
参数。我的最终代码:
public static IQueryable<TFilteredItem> ApplyFilter<TFilteredItem, TEnum>(IQueryable<TFilteredItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TFilteredItem, IEnumerable<TEnum>, bool>> predicate)
{
ParameterExpression itemParam = predicate.Parameters[0];
ParameterExpression enumsParam = predicate.Parameters[1];
var em = new ExpressionModifier<IEnumerable<TEnum>>(enumsParam.Name, enumValues);
Expression predicateBody = em.Modify(predicate.Body);
return items.Where(Expression.Lambda<Func<TFilteredItem, bool>>(predicateBody, new[] { itemParam }));
}
public class ExpressionModifier<T> : ExpressionVisitor
{
private readonly string parameterName;
private readonly T newValue;
public Expression Modify(Expression expression)
{
return Visit(expression);
}
public ExpressionModifier(string parameterName, T newValue)
{
this.parameterName = parameterName;
this.newValue = newValue;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == this.parameterName ? Expression.Constant(this.newValue, node.Type) : base.VisitParameter(node);
}
}
我使用它的方式如下:
var requests = this.ServiceManager.Messaging.GetRequests();
var states = new[] {RequestState.Active};
requests = ApplyFilter(requests, states, (i, e) => e.Contains(i.State));
我想要通过 IEnumerable<TEnum>
过滤 Entity Framework IQueryable<TItem>
的通用方法。它的签名大概应该是这样的:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TItem, IEnumerable<TEnum>, bool>> predicate)
{
return items.Where(??????);
}
我希望能够像这样调用它:
IQueryable<Request> requests = service.GetAllRequests();
IEnumerable<RequestState> states = new RequestState[] {RequestState.Active, RequestState.Closed};
Expression<Func<Request, IEnumerable<RequestState>, bool>> predicate = (r, s) => s.Contains(r.State);
requests = ApplyFilter(requests, states, predicate);
但是方法体内应该有什么?如何将 Expression<Func<TItem, IEnumerable<TEnum>, bool>>
转换为 Expression<Func<TItem, bool>>
以用作 "Where" 方法的参数?它可以与 Entity Framework 一起使用吗?
IMO,谓词应该在你的 ApplyFilter
方法中(考虑到问题)。
一种可能的编码方式是:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items,
IEnumerable<TEnum> enumValues, Expression<Func<TItem,TEnum>> enumField)
{
return items.Where(i => enumValues.Contains(enumField(i)));
}
这样的电话:
requests = ApplyFilter(requests, states, r => r.State);
当我在实施 Sharped 的回答时,我发现实际上可以完全按照我的意愿去做。重点是使用继承自 ExpressionVisitor 的自定义 class。此 class 将用其实际值替换表达式树中每个出现的 IEnumerable<TEnum>
参数。我的最终代码:
public static IQueryable<TFilteredItem> ApplyFilter<TFilteredItem, TEnum>(IQueryable<TFilteredItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TFilteredItem, IEnumerable<TEnum>, bool>> predicate)
{
ParameterExpression itemParam = predicate.Parameters[0];
ParameterExpression enumsParam = predicate.Parameters[1];
var em = new ExpressionModifier<IEnumerable<TEnum>>(enumsParam.Name, enumValues);
Expression predicateBody = em.Modify(predicate.Body);
return items.Where(Expression.Lambda<Func<TFilteredItem, bool>>(predicateBody, new[] { itemParam }));
}
public class ExpressionModifier<T> : ExpressionVisitor
{
private readonly string parameterName;
private readonly T newValue;
public Expression Modify(Expression expression)
{
return Visit(expression);
}
public ExpressionModifier(string parameterName, T newValue)
{
this.parameterName = parameterName;
this.newValue = newValue;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == this.parameterName ? Expression.Constant(this.newValue, node.Type) : base.VisitParameter(node);
}
}
我使用它的方式如下:
var requests = this.ServiceManager.Messaging.GetRequests();
var states = new[] {RequestState.Active};
requests = ApplyFilter(requests, states, (i, e) => e.Contains(i.State));