2013-03-30 12 views
7

Widziałem, że można dodać razem skompilowane metody.Jaki jest cel dodawania połączonych metod Func?

Expression<Func<Customer, bool>> ln = c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase); 
Expression<Func<Customer, bool>> fn = c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase); 
Expression<Func<Customer, bool>> fdob = c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd")); 

var filter = ln.Compile() + fn.Compile() + fdob.Compile(); 

Czy ma to sens?

bym zamierzają użyć filtra w miejsce wyrażenia lambda do filtrowania repozytorium klientów:

IEnumerable<Customer> customersFound = _repo.Customers.Where(filter); 

zależności od logiki biznesowej, to może lub nie może dodać trzy opracowanych metod razem, ale pick i wybierz, i ewentualnie dodaj więcej skompilowanych metod, jak idę.

Czy ktoś może wyjaśnić, że dodanie ich razem utworzyłoby ciąg zapytania, czy nie? Czy ktoś ma lepszą sugestię?

Mogę łączyć zdania "Gdzie" i używać wyrażeń lambda, ale jestem zaintrygowany tym, co możesz zyskać na kompilacji metod i dodaniu ich!

Odpowiedz

3

Jest to twórczy wysiłek orzeczników łańcuchowych i tak źle to nie działa. Powód, dla którego świetnie wytłumaczyli to Lasse i Nicholas (+2). Ale ty też pytasz:

Ktoś ma lepszą propozycję?

Wadą skompilowanych wyrażeń jest to, że nie jesteśmy już więcej wyrażeń, a zatem nie mogą być używane z IQueryable s, które są wspierane przez operatorów zapytań SQL (jak LINQ do SQL lub LINQ do podmiotów). Zakładam, że _repo.Customers jest takim IQueryable.

Jeśli chcesz dynamicznie łańcuchować wyrażenia, LINQKit's PredicateBuilder jest doskonałym narzędziem do do this.

var pred = Predicate.True<Customer>(); 

pred = pred.And(c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase); 
pred = pred.And(c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase); 
pred = pred.And(c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd")); 

var customersFound = _repo.Customers.Where(pred.Expand()); 

To właśnie Expand jest dla:

przetwarzanie zapytań rurociąg Entity Framework nie obsługuje wyrażeń wywołaniu, dlatego trzeba zadzwonić AsExpandable pierwszego obiektu w zapytaniu. Wywołując AsExpandable, aktywujesz klasę wyrażeń użytkownika LINQKit, która zastępuje wyrażenia wywołania prostszymi konstrukcjami, które można zrozumieć w Entity Framework.

lub without to wyrażenie jest Invoke d, która powoduje wyjątek EF

LINQ typu węzła wyrażenie „Wywołanie” nie jest obsługiwana w LINQ podmiotów.

BTW. Jeśli użyjesz linq do sql/encji, równie dobrze możesz użyć c.lastname == _customer.lastName, ponieważ jest ono tłumaczone na SQL, a sortowanie w bazie danych będzie miało wpływ na rozróżnianie wielkości liter.

+0

Dziękuję Gert, bardzo pomocna. Myślę, że zainstaluję LinqKit w moim projekcie i spróbuję. Szkoda, dodając, że delegaci nie budują takiego filtra, jak się spodziewałem, ale LinqKit wygląda jak dobre rozwiązanie i wygląda dość czysto syntaktycznie. Mój projekt używa EF5 i SQL2012 na wypadek, gdyby ktoś się zastanawiał. –

+0

Lasse i Nicholas wyjaśnili o dodaniu delegatów do multiemisji, ale sugerowane przez Gert rozwiązanie najlepiej odpowiadało: "Ktoś ma lepszą sugestię?". Wdrożyłem rozwiązanie przy użyciu LinqKit zgodnie z sugestią. Dzięki, o) –

7

Jest to efekt uboczny faktu, że metoda Compile zwraca delegata.

Wewnętrznie, delegat jest delegatem multiemisji, może zawierać wiele powiązanych odwołań do metod.

W tym przypadku jest to tylko szybki sposób na ich połączenie. Więcej informacji o łączeniu delegatów można znaleźć na stronie MSDN pod numerem How to: Combine Delegates.

Oto program LINQPad który demonstruje:

void Main() 
{ 
    var filter = GetX() + GetY() + GetZ(); 
    filter().Dump(); 
} 

public int X() { Debug.WriteLine("X()"); return 1; } 
public int Y() { Debug.WriteLine("Y()"); return 2; } 
public int Z() { Debug.WriteLine("Z()"); return 3; } 

public Func<int> GetX() { return X; } 
public Func<int> GetY() { return Y; } 
public Func<int> GetZ() { return Z; } 

wyjściowa:

X() 
Y() 
Z() 
3 

Zauważ, że wydaje się zatem, aby po prostu zignorować wartości zwracanej od pierwszych wywołań metod i zwraca tylko ostatni, chociaż w pełni nazywa to również wcześniejszymi metodami.

Zauważ, że powyższy kod jest podobny do tego:

void Main() 
{ 
    Func<int> x = X; 
    Func<int> y = Y; 
    Func<int> z = Z; 

    var filter = x + y + z; 
    filter().Dump(); 
} 

public int X() { Debug.WriteLine("X()"); return 1; } 
public int Y() { Debug.WriteLine("Y()"); return 2; } 
public int Z() { Debug.WriteLine("Z()"); return 3; } 

Krótka odpowiedź brzmi zatem, że nie, to nie to, co chcesz to zrobić.

+0

Dziękuję Lasse. Dowody, które widziałem wskazywały, że tylko ostatnia metoda w łańcuchu została użyta do filtrowania, ale nie byłem w 100% pewny wewnętrznych elementów. –

5

Po wywołaniu Compile() tworzone są instancje typu Func<Customer, bool>, który jest delegatem.

Delegaci przeciążają numer operator+, aby zwrócić uczestników multiemisji, co właśnie tutaj się dzieje.

See MSDN: How to: Combine Delegates (Multicast Delegates)

Tak, no - dodając je razem nie zbudować ciąg kwerendy.


Jeśli dotyczy EF, należy użyć drzew wyrażeń, a nie Lambdas.

Można tworzyć i modyfikować Expression Trees pomocą System.Linq.Expressions nazw:

MSDN: System.Linq.Expressions Namespace

i

MSDN: How to: Use Expression Trees to Build Dynamic Queries

+0

Dziękuję Nicholas, będę śledzić twoje linki. Próbowałem szukać google w poszukiwaniu odpowiedzi, ale zakładam, że szukałem z niewłaściwymi warunkami. Bardzo zobowiązany. –

+0

@IanDangerRobertson Zapraszamy i dziękujemy za interesujące pytanie :) –