2012-01-23 17 views
13

Próbuję utworzyć wyrażenie lambda, które zostanie połączone z innymi w dość dużym drzewie wyrażeń do filtrowania. Działa to dobrze, dopóki nie będę musiał filtrować według właściwości kolekcji podrzędnej.Budowanie drzewa ekspresji dynamicznej w celu filtrowania właściwości kolekcji

Jak zbudować wyrażenie Lambda, które będzie filtrować przy użyciu Any() na właściwości kolekcji, która jest właściwością obiektu głównego?

Przykład:

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

ten sposób chciałbym zbudować wyrażenie statycznie, ale trzeba ją zbudować dynamicznie. Przepraszam za zamieszanie.

Edycja: Oto fragment o tym, jak poradzić sobie z mniej skomplikowanych wyrażeń:

IQueryable<Office> officeQuery = CurrentDataSource.Offices.AsQueryable<Office>(); 
ParameterExpression pe = Expression.Parameter(typeof(Office), "Office"); 
ParameterExpression tpe = Expression.Parameter(typeof(Trades), "Trades"); 

Expression SimpleWhere = null; 
Expression ComplexWhere = null; 
foreach (ServerSideFilterObject fo in ssfo) 
{ 
    SimpleWhere = null; 
    foreach (String value in fo.FilterValues) 
    { 
     if (!CollectionProperties.Contains(fo.PropertyName)) 
     { 
      //Handle singleton lambda logic here. 
      Expression left = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression right = Expression.Constant(value); 
      if (SimpleWhere == null) 
      { 
       SimpleWhere = Expression.Equal(left, right); 
      } 
      else 
      { 
       Expression e1 = Expression.Equal(left, right); 
       SimpleWhere = Expression.Or(SimpleWhere, e1); 
      } 
     } 
     else 
     { 
      //handle inner Collection lambda logic here. 
      Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
      Expression right = Expression.Constant(value); 
      Expression InnerLambda = Expression.Equal(left, right); 

      //Problem area. 
      Expression OfficeAndProperty = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression OuterLambda = Expression.Call(OfficeAndProperty, typeof(Trades).GetMethod("Any", new Type[] { typeof(Expression) }),InnerLambda); 

      if (SimpleWhere == null) 
       SimpleWhere = OuterLambda; 
      else 
       SimpleWhere = Expression.Or(SimpleWhere, OuterLambda); 
     } 
    } 
    if (ComplexWhere == null) 
     ComplexWhere = SimpleWhere; 
    else 
     ComplexWhere = Expression.And(ComplexWhere, SimpleWhere); 
} 
MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { officeQuery.ElementType }, officeQuery.Expression, Expression.Lambda<Func<Office, bool>>(ComplexWhere, new ParameterExpression[] { pe })); 
results = officeQuery.Provider.CreateQuery<Office>(whereCallExpression); 
+0

Czy pytasz, jak zbudować drzewo wyrażeń? – SLaks

+0

Nie jestem pewien, jak działa hierarchia w twoim przykładzie. Czy możesz rozwinąć trochę więcej na ten temat? Czy urzędy są korzeniami, a następnie każdy Urząd ma kolekcję Transakcji? A chcesz filtrować nazwę handlu? Filtr jest tam, gdzie jestem trochę zagubiony. Przepraszam. –

+0

Nie, po prostu nie jestem pewien składni używanej do budowania wyrażenia z wewnętrznym wywołaniem metody i wyrażeniem dla parametru. W tym przypadku otrzymuję komunikat o błędzie stwierdzający, że nie można znaleźć Any(), ponieważ moje parametry nie pasują do definicji. W tym przypadku nie jestem pewien, czy to dlatego, że jestem wyłączony na składni lub jeśli Any() nie jest obsługiwany w sposób, w jaki go używam. – George

Odpowiedz

9

znaleziono rozwiązanie. Nie szukałem żadnej metody we właściwym miejscu wcześniej.

Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
Expression right = Expression.Constant(value); 
Expression InnerLambda = Expression.Equal(left, right); 
Expression<Func<Trades, bool>> innerFunction = Expression.Lambda<Func<Trades, bool>>(InnerLambda, tpe); 

method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(Trades)); 
OuterLambda = Expression.Call(method, Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)),innerFunction); 
0

Co podane jako przykład będzie działać w oparciu o komentarz. Oto przykład tego, co pracuję z:

Templates.Where(t => t.TemplateFields.Any(f => f.Required == 'Y')) 

Mamy szablonów, które mają specyficzny zbiór pól i może być konieczne tych dziedzinach. Mogę więc uzyskać szablony, w których wymagane jest pole wymagane przez powyższe stwierdzenie.

Mam nadzieję, że to pomaga ... lub przynajmniej potwierdza to, co próbujesz zrobić. Daj mi znać, jeśli będziesz mieć więcej pytań na ten temat, a rozwiążę tę kwestię.

Powodzenia!

+0

Jest to podobne do tego, z czym pracuję, ale muszę dynamicznie budować wyrażenie lamda z odbiciem, aby upewnić się, że filtr zawiera inne filtry w zestawie. – George

0

Podany kod

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

powinna pracować, tak długo, jak o.base_Trades realizuje IEnumerable<Trade>. Jeśli o.base_Trades implementuje tylko IEnumerable, musisz użyć albo Cast<Trade>(), jeśli możesz być pewien, że wszystkie elementy w o.base_Trades są typu Trade lub OfType<Trade>(), jeśli mogą występować elementy innych (niekompatybilnych) typów.

To byłoby wtedy wyglądać następująco:

CurrentDataSource.Offices 
    .Where(o => o.base_Trades.Cast<Trade>.Any(t => t.Name == "test")) 
1

Nie rób tego, co naprawdę chcesz, aby użyć biblioteki o nazwie dynamq linq. http://nuget.org/packages/DynamicLINQ

Można po prostu przechowywać zapytania jako ciągi i obsługuje on bardzo złożone zapytania. Drzewa ekspresji to koszmar.

+0

która jest doskonałą biblioteką, ja też jej używam, ale nie obsługuje na przykład sortowania we właściwości kolekcji, takiej jak 'myIQueryable.OrderBy (x => x.MyCollection.Select (y => y.Myproperty)) "przynajmniej nie mogę go uruchomić –

Powiązane problemy