2016-10-24 7 views
1

Я создаю расширенный поиск, который преобразует выражение OData в дерево выражений .NET (Expression<Func<T, bool>>). Я передаю это выражение в свой метод EF6 .Select() как предикат, и он работает так, как ожидалось.Выбор из типа DbSet с использованием .Set() вместо .Set <T>()

Однако при реализации этой функции я обнаружил, что методы LINQ работают только с IQueryable<TSource>. Это работает для .Set<T>(), но я не буду знать тип во время выполнения, поэтому мне нужно использовать .Set().

Возможно, я мог бы использовать отражение для вызова .Set<T>(), а затем вызвать его, но это похоже на взломать, поэтому я скорее сделаю это прямо через .Set(), если это вообще возможно.

+0

Как создать 'Expression > 'если вы не знаете' T'? –

+0

@IvanStoev Вы можете создавать выражения с использованием типов. – oscilatingcretin

+0

Как не общий термин 'Expression.Lambda', который возвращает' LambdaExpression'? И вы хотите привязать его к 'Where'? –

ответ

1

Если я правильно понимаю, у вас есть LambdaExpression вместо Expression<Func<T, bool>> и вы хотите использовать его как Where но на IQueryable (который DbSet класс орудий), а не IQueryable<T>.

Все, что вам нужно знать, это то, что методы расширения IQueryable<T> просто испускают MethodCallExpression соответствующему методу Queryable в дереве выражений запроса.

Например, чтобы эмулировать Where или Select на IQueryable вы можете использовать следующие методы пользовательских расширений:

public static class QueryableExtensions 
{ 
    public static IQueryable Where(this IQueryable source, LambdaExpression predicate) 
    { 
     var expression = Expression.Call(
      typeof(Queryable), "Where", 
      new Type[] { source.ElementType }, 
      source.Expression, Expression.Quote(predicate)); 
     return source.Provider.CreateQuery(expression); 
    } 

    public static IQueryable Select(this IQueryable source, LambdaExpression selector) 
    { 
     var expression = Expression.Call(
      typeof(Queryable), "Select", 
      new Type[] { source.ElementType, selector.Body.Type }, 
      source.Expression, Expression.Quote(selector)); 
     return source.Provider.CreateQuery(expression); 
    } 
} 

Вы можете сделать то же для других Queryable методов, необходимых.

Update: Поскольку вы интересны, вот пример использования выражения прототипов, чтобы получить общее определение метода и построить обобщенный метод из него:

public static class QueryableExtensions 
{ 
    static MethodInfo QueryableMethod<T>(this Expression<Func<IQueryable<object>, T>> prototype, params Type[] types) 
    { 
     return ((MethodCallExpression)prototype.Body).Method 
      .GetGenericMethodDefinition() 
      .MakeGenericMethod(types); 
    } 

    public static IQueryable Where(this IQueryable source, LambdaExpression predicate) 
    { 
     var expression = Expression.Call(
      QueryableMethod(q => q.Where(x => true), source.ElementType), 
      source.Expression, Expression.Quote(predicate)); 
     return source.Provider.CreateQuery(expression); 
    } 

    public static IQueryable Select(this IQueryable source, LambdaExpression selector) 
    { 
     var expression = Expression.Call(
      QueryableMethod(q => q.Select(x => 1), source.ElementType, selector.Body.Type), 
      source.Expression, Expression.Quote(selector)); 
     return source.Provider.CreateQuery(expression); 
    } 
} 
+0

Это потрясающе. Работает отлично. Любая идея, как получить MethodInfo методов LINQ с помощью выражений, чтобы вам не пришлось использовать строку? Я пробовал пару часов, но повесил трубку на соответствие подписям перегрузок, которые используют общие типы (обычно вы можете просто передать тип по умолчанию, но я не знаю, как это сделать с помощью дженериков). – oscilatingcretin

+0

Зачем беспокоиться с MethodInfo - имена методов не будут меняться, а также если вы находитесь на C# 6, вы всегда можете использовать 'nameof (Queryable.Select)' –

+0

Клянусь, я попробовал имя, но это дало мне некоторые проблемы , Просто попробовал еще раз, и он работает, поэтому я делал что-то неправильно. Согласитесь, имена никогда не изменятся, но я стараюсь избегать магических струн, где это возможно. Еще раз спасибо – oscilatingcretin