2015-03-21 6 views
0

Я хотел бы иметь общий метод фильтрации Entity Framework IQueryable<TItem> по IEnumerable<TEnum>. Его подпись должна, вероятно, выглядеть следующим образом:Общий метод запроса Entity Framework

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>> для использования в качестве параметра метода «Где»? Будет ли он работать с Entity Framework?

ответ

0

Когда я реализую ответ обострял, я обнаружил, что на самом деле можно сделать именно то, что я хочу. Дело в том, что пользовательский класс наследуется от ExpressionVisitor. Этот класс заменит каждое вхождение параметра 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)); 
0

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); 
+0

Да, это, вероятно, решение, которое я буду использовать. Я просто надеялся получить что-то еще более общее, то есть не ограничиваться методом «Содержит». Спасибо, что ответили. –

Смежные вопросы