Aby zwrócić IQueryable lub nie zwrócić IQueryable

Mam klasę repozytorium, która zawija mój LINQ do kontekstu danych SQL. Klasa repozytorium jest klasą linii biznesowej, która zawiera całą logikę warstwy danych (oraz buforowanie itp.).

Oto moja v1 mojego interfejsu repo.
public interface ILocationRepository
{
    IList<Location> FindAll();
    IList<Location> FindForState(State state);
    IList<Location> FindForPostCode(string postCode);
}

Ale aby poradzić sobie z pagingiem dla FindAll, zastanawiam się, czy ujawnić IQueryable zamiast IList, aby uprościć interfejs dla okoliczności takich jak paging.

Jakie są plusy i minusy odsłaniania IQueryable z danych repo?

Każda pomoc jest bardzo mile widziana.
Author: CVertex, 2009-04-05

3 answers

Plusy; Skład:

  • wywołujący mogą dodawać filtry
  • rozmówcy mogą dodawać paging
  • wywołujący mogą dodać sortowanie
  • etc

Wady; nietestowalność:

  • Twoje repozytorium nie jest już poprawnie testowane jednostkowo; nie możesz polegać na a: działa, b: co robi ;
      Nie jest to jednak możliwe w przypadku, gdy wywołanie nie jest możliwe do przetlumaczenia (np. brak mapowania TSQL; przerwy w uruchomieniu).]}
    • rozmówca może dodać filtr/sortowanie, które sprawia, że wykonuj jak pies
  • ponieważ wywołujący oczekują, że IQueryable<T> będą komponowalne, wyklucza to nie-komponowalne implementacje - lub zmusza cię do napisania własnego dostawcy zapytań dla nich
  • to znaczy, że nie możesz optymalizować / profilować DAL

Dla stabilności, wziąłem Nie wystawianie IQueryable<T> lub Expression<...> w moich repozytoriach. Oznacza to, że wiem, jak zachowuje się repozytorium, a moje górne warstwy mogą używać mocks bez martwienia się "czy rzeczywiste repozytorium to obsługuje?" (wymuszanie testów integracyjnych).

Nadal używam IQueryable<T> etc Wewnątrz repozytorium - ale nie poza granicami. Zamieściłem kilka więcej myśli na ten temat tutaj . Równie łatwo jest umieścić parametry stronicowania w interfejsie repozytorium. Można nawet użyć metod rozszerzenia (w interfejsie), aby dodać opcjonalne parametry stronicowania , tak aby konkretne klasy miały tylko 1 metodę do zaimplementowania, ale mogą być 2 lub 3 przeciążenia dostępne dla wywołującego.

 89
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-05 09:20:10

Jak wspomniano w poprzedniej odpowiedzi, ujawnienie IQueryable daje dostęp do wywołujących do gry z IQueryable się, co jest lub może stać się niebezpieczne.

Hermetyzacja logiki biznesowej pierwszym zadaniem jest utrzymanie integralności bazy danych.

Możesz nadal wystawiać IList i możesz zmienić swoje parametry w następujący sposób, tak to robimy...

public interface ILocationRepository
{
    IList<Location> FindAll(int start, int size);
    IList<Location> FindForState(State state, int start, int size);
    IList<Location> FindForPostCode(string postCode, int start, int size);
}

If size = = -1 then return all...

Alternatywny sposób...

Jeśli nadal chcesz zwrócić IQueryable, a następnie możesz zwrócić IQueryable listy wewnątrz swoich funkcji.. na przykład...

public class MyRepository
{
    IQueryable<Location> FindAll()
    {
        List<Location> myLocations = ....;
        return myLocations.AsQueryable<Location>;
        // here Query can only be applied on this
        // subset, not directly to the database
    }
}

Pierwsza metoda ma przewagę nad pamięcią, ponieważ zwrócisz mniej danych zamiast wszystkich.

 7
Author: Akash Kava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-05 09:55:45

Zalecam użycie IEnumerable zamiast IList, dzięki niemu będziesz miał większą elastyczność.

W ten sposób będziesz mógł uzyskać z Db tylko tę część danych, której naprawdę użyjesz bez dodatkowej pracy w repozytorium.

Próbka:

// Repository
public interface IRepository
{
    IEnumerable<Location> GetLocations();
}

// Controller
public ActionResult Locations(int? page)
{
    return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}
Co jest bardzo czyste i proste.
 2
Author: Konstantin Tarkus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-05 10:11:38