2014-03-26 2 views
2

저는 네비게이션 속성을 사용하여 Entity Framework 및 쿼리를 동적으로 생성합니다. 내 사용 사례의 대부분을중첩 컬렉션 속성에서 필터링 할 동적 식 트리

다음 잘 작동합니다 :

private static MethodCallExpression GetNavigationPropertyExpression<T>(string propertyName, int test, 
     ParameterExpression parameter, string subParameter) 
    { 
     var navigationPropertyCollection = Expression.Property(parameter, propertyName); 

     var childType = navigationPropertyCollection.Type.GetGenericArguments()[0]; 

     var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(childType); 

     var aclAttribute = GetAclAttribute(typeof(T), propertyName); 
     var childProperty = aclAttribute.ChildProperty; 

     var propertyCollectionGenericArg = childType; 
     var serviceLocationsParam = Expression.Parameter(propertyCollectionGenericArg, subParameter); 

     var left = Expression.Property(serviceLocationsParam, childProperty); 
     var right = Expression.Constant(test, typeof(int)); 

     var isEqual = Expression.Equal(left, right); 
     var subLambda = Expression.Lambda(isEqual, serviceLocationsParam); 

     var resultExpression = Expression.Call(anyMethod, navigationPropertyCollection, subLambda); 

     return resultExpression; 
    } 

내가 정의 AclAttribute 클래스 메타 데이터의 종류와 부분 클래스를 통해 속성에 할당을 사용합니다. 네비게이션 프로퍼티의 경우, ChildProperty가 제공되어 표현식 작성기가 원하는 속성을보다 자세히 알 수 있습니다.

예 : 테이블 서비스는 ServiceLocations라는 다른 테이블을 참조합니다. ServiceLocations 참조에서 LocationId 값이 필요합니다. 이 부분은 잘 작동합니다.

내 문제는 통과 할 중첩 된 속성이 두 개 이상있는 경우입니다. 또 다른 예로 : ServiceCategories 테이블은 ServiceLocations를 다시 참조하는 Services를 참조합니다. 이전과 마찬가지로 ServiceLocations에서 LocationId 값이 필요합니다.

두 가지 "모든"방법을 사용하여 결과를 결합하고 결과를 반환하지만 수동으로 수행했지만 탐색 속성이 컬렉션이 아니거나 탐색 속성의 자식이 수집. 이러한 경우 재귀 옵션이 필요합니다. 나는 잠시 동안 노력해 왔고, 짧아지고있다.

나는 그것을 언급 했으므로 여기 두 번째 예제를 위해 함께 테스트 해 보았습니다. 이 방법은 효과가 있지만 DRY를 위반합니다. (참고 : 나는 테이블의 이름을 지정하지 않은) :이

private static MethodCallExpression GetNestedNavigationPropertyExpression(int test, ParameterExpression rootParameter) 
    { 
     var servicesProperty = Expression.Property(rootParameter, "tblServices"); 
     var servicesParameter = Expression.Parameter(servicesProperty.Type.GetGenericArguments()[0], "ss"); 

     var serviceLocationsProperty = Expression.Property(servicesParameter, "tblServiceLocations"); 
     var serviceLocationsParameter = Expression.Parameter(serviceLocationsProperty.Type.GetGenericArguments()[0], "s"); 

     var servicesAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(servicesProperty.Type.GetGenericArguments()[0]); 
     var serviceLocationsAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(serviceLocationsProperty.Type.GetGenericArguments()[0]); 

     var aclAttribute = GetAclAttribute(typeof(tblService), "tblServiceLocations"); 

     var left = Expression.Property(serviceLocationsParameter, aclAttribute.ChildProperty); 
     var right = Expression.Constant(test, typeof(int)); 

     var isEqual = Expression.Equal(left, right); 
     var subLambda = Expression.Lambda(isEqual, serviceLocationsParameter); 

     var endExpression = Expression.Call(serviceLocationsAnyMethod, serviceLocationsProperty, subLambda); 
     var intermediaryLamba = Expression.Lambda(endExpression, servicesParameter); 
     var resultExpression = Expression.Call(servicesAnyMethod, servicesProperty, intermediaryLamba); 

     return resultExpression; 
    } 

답변

8

은 당신의 쿼리를 구축 할 수 있어야한다 :

public static Expression GetNavigationPropertyExpression(Expression parameter, int test, params string[] properties) 
{ 
    Expression resultExpression = null; 
    Expression childParameter, navigationPropertyPredicate; 
    Type childType = null; 

    if (properties.Count() > 1) 
    { 
     //build path 
     parameter = Expression.Property(parameter, properties[0]); 
     var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type); 
     //if it´s a collection we later need to use the predicate in the methodexpressioncall 
     if (isCollection) 
     { 
      childType = parameter.Type.GetGenericArguments()[0]; 
      childParameter = Expression.Parameter(childType, childType.Name); 
     } 
     else 
     { 
      childParameter = parameter; 
     } 
     //skip current property and get navigation property expression recursivly 
     var innerProperties = properties.Skip(1).ToArray(); 
     navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter, test, innerProperties); 
     if (isCollection) 
     { 
      //build methodexpressioncall 
      var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2); 
      anyMethod = anyMethod.MakeGenericMethod(childType); 
      navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate); 
      resultExpression = MakeLambda(parameter, navigationPropertyPredicate); 
     } 
     else 
     { 
      resultExpression = navigationPropertyPredicate; 
     } 
    } 
    else 
    { 
     //Formerly from ACLAttribute 
     var childProperty = parameter.Type.GetProperty(properties[0]); 
     var left = Expression.Property(parameter, childProperty); 
     var right = Expression.Constant(test, typeof(int)); 
     navigationPropertyPredicate = Expression.Equal(left, right); 
     resultExpression = MakeLambda(parameter, navigationPropertyPredicate); 
    } 
    return resultExpression; 
} 

private static Expression MakeLambda(Expression parameter, Expression predicate) 
{ 
    var resultParameterVisitor = new ParameterVisitor(); 
    resultParameterVisitor.Visit(parameter); 
    var resultParameter = resultParameterVisitor.Parameter; 
    return Expression.Lambda(predicate, (ParameterExpression)resultParameter); 
} 

private class ParameterVisitor : ExpressionVisitor 
{ 
    public Expression Parameter 
    { 
     get; 
     private set; 
    } 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     Parameter = node; 
     return node; 
    } 
} 

이 좋아 전화 :

var parameter = Expression.Parameter(typeof(A), "A"); 
var expression = ExpressionBuilder.GetNavigationPropertyExpression(parameter, 8,"CollectionOfB", "CollectionOfC", "ID"); 
+0

우수함! 잘 했어! 도와 주셔서 감사합니다! – Bill