2010-05-06 11 views
9

Mam interfejs o nazwie ICatalog, jak pokazano poniżej, gdzie każdy ICatalog ma nazwę i metodę, która zwróci elementy oparte na funkcji Predicate<Item>.Oparte na Linq generyczne alternatywne do Predicate <T>?

public interface ICatalog 
{ 
    string Name { get; } 
    IEnumerable<Item> GetItems(Predicate<Item> predicate); 
} 

Określona implementacja katalogu może być powiązana z katalogami w różnych formatach, takich jak XML lub baza danych SQL.

W katalogu XML kończę deserializację całego pliku XML do pamięci, więc testowanie każdego elementu za pomocą funkcji predykatu nie powoduje zwiększenia obciążenia, ponieważ jest już w pamięci.

Jednak w przypadku implementacji SQL raczej nie będę pobierał całej zawartości bazy danych do pamięci, a następnie filtruje elementy za pomocą funkcji predykatu. Zamiast tego chciałbym znaleźć sposób, aby jakoś przekazać predykat do serwera SQL, lub jakoś przekonwertować go na zapytanie SQL.

To wydaje się być problemem, który można rozwiązać za pomocą Linq, ale jestem całkiem nowy. Czy mój interfejs powinien zamiast tego zwracać IQueryable? Nie chodzi mi teraz o to, jak faktycznie zaimplementować wersję SQL mojego ICatalog. Po prostu chcę się upewnić, że mój interfejs pozwoli na to w przyszłości.

Odpowiedz

7

Rob wskazał, w jaki sposób możesz to zrobić (chociaż bardziej klasyczne podejście LINQ może zająć Expression<Func<Item,bool>>, i ewentualnie wrócić IQueryable<IFamily>).

Dobrą wiadomością jest to, że jeśli chcesz użyć predykatu z LINQ-obiekty (dla scenariusza XML) można następnie wystarczy użyć:

Predicate<Item> func = predicate.Compile(); 

lub (dla drugiego podpisu):

Func<Item,bool> func = predicate.Compile(); 

i masz delegata (func) do przetestowania obiektów.

Problemem jest jednak to, że jest to koszmar do testów jednostkowych - można tylko naprawdę integracja go przetestować.

Problem polega na tym, że nie można niezawodnie symulować (za pomocą LINQ-to-Objects) niczego, co dotyczy złożonych magazynów danych; na przykład, następujące będzie działać dobrze w badaniach jednostkowych, ale nie będzie działać „na poważnie” z bazą danych:

var foo = GetItems(x => SomeMagicFunction(x.Name)); 

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not 

Problem polega na tym, że tylko niektóre operacje mogą być tłumaczone na TSQL.Ten sam problem występuje z IQueryable<T> - na przykład EF i LINQ-SQL obsługują różne operacje na zapytaniu; nawet po prostu First() zachowuje się inaczej (EF wymaga, abyś jednoznacznie to zamówił, a LINQ-SQL nie).

Tak w skrócie:

  • może pracować
  • ale zastanów się dobrze, czy chcesz to zrobić; bardziej klasyczny interfejs repozytorium czarna skrzynka/usługi mogą być bardziej sprawdzalne
+0

Dzięki za odpowiedź Marc. Brzmi bardziej skomplikowanie, niż chciałbym robić to, co próbuję zrobić. Co rozumiesz przez "bardziej klasyczny repozytorium/interfejs serwisowy czarnej skrzynki"? Czy możesz podać przykład? –

5

Nie trzeba iść na całość i stworzyć IQueryable implementation

Jeśli zadeklarujesz metodę GetItems jak:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate); 

Następnie klasa wykonawczych może skontrolować wyrażenie do określenia tego, co jest spytał.

Przeczytać artykuł o IQueryable, ponieważ wyjaśnia, w jaki sposób zbudować gościa drzewa wyrażenie, które trzeba będzie zbudować prostą wersję.

+0

FYI: Spieprzyłem oryginalny przykład powinien mieć zarówno używać zarówno IEnumerable i orzecznik nie IEnumerable

Powiązane problemy