2009-09-23 12 views
6

Mam problem z niektórymi klasami generowanymi przez dbml, które nie pozwalają na sprawne działanie SQL. Wyobraź sobie, że mam tabelę kont i tabelę transakcji, w której każda transakcja jest powiązana z określonym kontem. Załadowałem wszystko do dbml i out wyskakuje klasy konta i klasy transakcji. Klasa Account ma odwołanie do EntitySet do kolekcji Transakcji, która reprezentuje wszystkie transakcje na tym koncie. Słusznie.Przeciekająca abstrakcja w LINQ do SQL EntitySet

Załóżmy teraz, że chcę tylko transakcji dla bieżącego okresu rozliczeniowego. Więc dodać metodę tak:

public IEnumerable<Transaction> CurrentTransactions 
{ 
    get 
    { 
     DateTime dtStart = CurrentPeriod; 
     DateTime dtEnd = NextPeriod; 
     return 
      from t in Transactions 
      orderby t.date 
      where t.date >= CurrentPeriod && t.date <= NextPeriod 
      select t; 
    } 
} 

Wygląda ładnie i działa, ale SQL nie jest dobre: ​​

SELECT [t0].[id], [t0].[account_id], [t0].[date], [t0].[description], [t0].[amount], [t0].[sign] 
FROM [dbo].[transactions] AS [t0] 
WHERE [t0].[account_id] = @p0 

Ie: to ciągnąc cały zbiór transakcji w dół i przetwarzania go z LINQ dla obiektów. Próbowałem, aby usunąć klauzulę where, klauzulę orderby, zastępującą daty stałymi, ale wciąż wszystko działa po stronie klienta.

Dla porównania, próbowałem wywołanie kolekcję Transakcje bezpośrednio z kontekstu danych:

DateTime dtStart = account.CurrentPeriod; 
DateTime dtEnd = account.NextPeriod; 
IEnumerable<Transaction> trans= 
       from t in MyDataContext.Transactions 
       orderby t.date 
       where t.date >= dtStart && t.date <= dtEnd && t.account_id==iAccountID 
       select t; 

i działa pięknie:

SELECT [t0].[id], [t0].[account_id], [t0].[date], [t0].[description], [t0].[amount], [t0].[sign] 
FROM [dbo].[transactions] AS [t0] 
WHERE ([t0].[date] >= @p0) AND ([t0].[date] <= @p1) AND ([t0].[account_id] = @p2) 
ORDER BY [t0].[date] 

Więc po tym wszystkim, mam dwa pytania:

  1. Czy powyższe zachowanie zestawu EntactionsSet jest prawidłowe i/lub czy istnieje sposób jego rozwiązania?
  2. Jak zaimplementować powyższą "naprawę" jako metodę na mojej klasie konta. Klasa encji wygenerowana przez dbml nie ma dostępu do DataContext.

Odpowiedz

6

Po prostu nie mogę tego zrobić, niestety. Właściwości kolekcji generowane dla klas jednostek LINQ do SQL nie są IQueryable; dlatego wszelkie zapytania wykonywane na nich będą używać LINQ do Objects. Jest to zgodne z projektem. Jak słusznie zauważysz siebie, aby uzyskać efektywne zapytanie, musisz wykonać zapytanie na temat Transactions pobranego z twojego DataContext, ale nie masz go w swoim getto.

W tym momencie opcje są albo:

  • Zrób to metoda, która zajmuje DataContext jako argument; lub
  • użycie odbicie hackery przy sprowadzaniu kontekst - sama jednostka nie przechowywać go, ale EntitySet s na nim zrobić, choć pośrednio - naturalnie jest to wersja specyficzny, podatne na pęknięcia, itp

tej pory jak wiem, Entity Framework nie ma tego ograniczenia, ponieważ jego właściwościami kolekcji są: ObjectQuery<T> - czyli IQueryable.

+0

Właściwości kolekcji Entity Framework, przynajmniej od 2009 do teraz, nie są typu 'ObjectQuery ' ale typu 'EntityCollection ' (który jednak ma metodę przekształcania ich w 'ObjectQuery '). –

0

Zamiast tego przełącz się z IEnumerable na IQueryable, a Twój SQL zostanie zoptymalizowany tak, aby tylko pobierać na żądanie to, czego potrzebujesz.

+1

Nie będzie. Jego problemem jest to, że 'Transakcje' nie jest' IQueryable', i nie może nic z tym zrobić (jest to 'EntitySet' i musi pozostać jednym). –

+0

Tak, to jest dokładnie problem. –

+0

Argh. Nigdy nie jest to łatwe rozwiązanie. :) –

3

Jaki jest typ Transakcji w pierwszym przykładzie?

Pamiętaj, że używasz metod rozszerzeń. LINQ rozszerzeń Metody, które są używane są zależne od interfejsu Transakcje implementuje:

  • IQueryable <T> byłoby LINQ-SQL lub LINQ-podmioty lub ...
  • IEnumerable <T> da ci linq-to-objects.

Edit:

To papilarnych typu EntitySet:

public sealed class EntitySet<TEntity> : IList, 
    ICollection, IList<TEntity>, ICollection<TEntity>, IEnumerable<TEntity>, 
    IEnumerable, IListSource 
where TEntity : class 

Aby odpowiedzieć na Twoje pytania:

  1. Transakcje nie implementuje IQueryable <T> tak jest to prawidłowe zachowanie
  2. Twoja klasa konto trzeba będzie mógł odwołać się do obiektu Transakcje Tabela
+0

Transakcje to własność EntitySet utworzona przez dbml. \t \t [Związek (nazwa = "Account_Transaction" magazynowanie = "_ transakcji" ThisKey = "id", OtherKey = "ACCOUNT_ID")] \t \t publicznych EntitySet Transakcje –

+0

Odnośnie 2). Rozumiem to, problem polega na tym, jak uzyskać takie odniesienie, biorąc pod uwagę, że obiekty Konta są zwykle tworzone przez strukturę kontekstu danych/encji? Gdzie powinienem się podłączyć, aby przekazać obiekt konta jako odniesienie do tabeli transakcji? Lub umieścić inny sposób, biorąc pod uwagę obiekt podmiot, który jest związany z określonym kontekście danych, w jaki sposób uzyskać od tego obiektu z powrotem do jego kontekście danych? –

+0

Czy rozszerzasz funkcjonalność obiektu Konto, prawda? Dodać sparametryzowane wywołanie metody? IEnumerable GetCurrentTransactions (DataContext ctx) –