2016-10-24 8 views
1

OData 식을 .NET 표현 트리 (Expression<Func<T, bool>>)로 변환하는 고급 검색을 만들고 있습니다. 이 표현식을 EF6.Select() 메서드에 조건 자로 전달하면 예상대로 작동합니다..Set() 대신 .Set()을 사용하여 형식 기반 DbSet 선택 <T>()

그러나이 기능을 구현할 때 LINQ 메서드는 IQueryable<TSource>에서만 작동한다는 것을 발견했습니다. 이것은 .Set<T>()에서 작동하지만 런타임에 유형을 알 수 없으므로 .Set()을 사용해야합니다.

리플렉션을 사용하여 .Set<T>()을 호출 한 다음이를 호출 할 수는 있지만 해킹 비트처럼 보일 수 있으므로 가능한 경우 직접 .Set()을 통해 수행해야합니다. 만약 내가 제대로 이해하고

+0

당신이 '표현을 만들려면 어떻게해야합니까 >''T '를 모른다면? –

+0

@IvanStoev 유형을 사용하여 표현식을 만들 수 있습니다. – oscilatingcretin

+0

'LambdaExpression'을 반환하는 비 generic'Expression.Lambda'와 비슷합니까? 그리고 그것을 'Where'에 바인딩하고 싶습니까? –

답변

1

대신 Expression<Func<T, bool>>LambdaExpression을 가지고 있고 Where로 사용하고 싶지만 IQueryable (이 DbSet 클래스가 구현)에 오히려 IQueryable<T> 이상을.

여러분이 알아야 할 것은 IQueryable<T> 확장 메서드가 쿼리 식 트리의 메서드에 MethodCallExpression 메서드를 내보내는 것입니다.

예를 들어, IQueryableWhere 또는 Select는 다음과 같은 사용자 정의 확장 방법을 사용할 수 있습니다 에뮬레이션 :

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 방법에 대한 유사 할 수 있습니다.

업데이트 : 당신이 재미 있기 때문에가 여기에 제네릭 메서드 정의를 얻고 그것에서 일반적인 방법을 구축하는 식 프로토 타입을 사용하는 예제입니다

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

정말 멋지 네요. 완벽하게 작동합니다. LINQ 메서드의 MethodInfo를 사용하여 식을 사용하여 문자열을 사용할 필요가없는 방법에 대한 아이디어가 있습니까? 나는 몇 시간 동안 노력했지만, 범용 유형을 사용하는 오버로드 서명과 매치 될 때까지 매달 렸습니다. 보통은 기본 유형을 전달할 수 있지만 제네릭을 사용하여이를 수행하는 방법을 알지 못합니다. – oscilatingcretin

+0

왜 MethodInfo에 신경을 써야할까요? 메소드의 이름이 바뀌지 않을 것입니다. 또한 C# 6에서 항상 nameof (Queryable.Select)를 사용할 수 있습니다. –

+0

nameof를 시도했는데 맹세했습니다. . 그냥 다시 시도하고 작동, 그래서 뭔가 잘못하고있다. 동의합니다. 이름은 절대로 바뀌지 않지만 가능한 한 매직 문자열을 피하는 경향이 있습니다. 다시 한 번 감사합니다 – oscilatingcretin