2012-03-03 14 views
5

Niedawno zmarnowałem problem, aby dynamicznie tworzyć wyrażenia Linq podczas wykonywania. Większość przykładów, które znalazłem, dotyczy raczej prostego zadania polegającego na porównaniu jednej właściwości danej jednostki bazy danych z pojedynczym parametrem. Tak:Tworzenie wyrażenia Linq dynamicznie zawierającego podzapytanie

Session.Query.Where(m => m.Name.Contains("test")) 

które również mogą być osiągnięte ze znacznie bardziej ogólne podejście jak ten:

var item = Expression.Parameter(typeof (MyClass), "item"); 
var property = Expression.Property(item, "Name"); 
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
var searchExpression = Expression.Constant(searchString, typeof(string)); 
var containsMethodExpression = Expression.Call(property, containsMethod, searchExpression); 
var lambda = Expression.Lambda<Func<MyClass, bool>>(containsMethodExpression, item); 
query = query.Where(lambda);  

Czasami jednak zadanie jest nieco bardziej skomplikowane i chce się osiągnąć coś jak następuje:

Session.Query.Where(m => m.SpecialProperty.Any(f => f.Name.Contains("test"))); 

Gdzie „SpecialProperty” jest na liście typów <> i mienia „nazwa” jest typu ciąg.

Czy można tak dynamicznie budować wyrażenie Linq i jak można to osiągnąć? Czy istnieją jakieś obawy dotyczące wydajności tego podejścia?

Odpowiedz

6

Znaleziono rozwiązanie, które działa w moim konkretnym przypadku użycia i robi dokładnie to, czego szukałem.

/* 
building expression tree 
example: Session.Query.Where(m => m.SpecialProperty.Any(f => f.Name.Contains("test"))) 
*/ 

var innerItem = Expression.Parameter(typeof(MyInnerClass), "f"); 
var innerProperty = Expression.Property(innerItem, "Name"); 
var innerMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
var innerSearchExpression = Expression.Constant(searchString, typeof(string)); 
var innerMethodExpression = Expression.Call(innerProperty, innerMethod, new[] { innerSearchExpression }); 
var innerLambda = Expression.Lambda<Func<MyInnerClass, bool>>(innerMethodExpression, innerItem); 

var outerItem = Expression.Parameter(typeof(MyOuterClass), "m"); 
var outerProperty = Expression.Property(outerItem, info.Name); 
/* calling a method extension defined in Enumerable */ 
var outerMethodExpression = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(MyInnerClass) }, outerProperty, innerLambda); 
var outerLambda = Expression.Lambda<Func<MyOuterClass, bool>>(outerMethodExpression, outerItem); 
query = query.Where(outerLambda); 

To raczej błyskotliwy podejście jest potrzebne zamiast bardziej eleganckim pojedyncza linia LINQ Expression aby umożliwić parametryzacji typów i nazw metod. Jednak oczywiście nie mam nic przeciwko innym sugestiom i pomysłom dotyczącym możliwych kar za wyniki.

Jest bardzo prawdopodobne, że ten fragment kodu mógłby również pomóc w rozwiązaniu problemu How to produce a Subquery using non-generic Lambda.

Powiązane problemy