2009-06-24 13 views
5

Mam 3 rodzaje obiektów: Agencja, BusinessUnit i Klient (każdy z własną tabelą)Jak mogę zapytać o te dane hierarchiczne za pomocą LINQ?

Pod względem hierarchii Agencje własne BusinessUnits i BusinessUnits własnych klientów.

mam 3 C# POCO obiektów do ich reprezentowania (I zazwyczaj wybrać nowy {} do nich, zamiast używać LINQ generowane klas):

public class Agency 
{ 
    public IEnumerable<BusinessUnit> BusinessUnits { get; set; } 
} 

public class BusinessUnit 
{ 
    public IEnumerable<Client> Clients { get; set; } 
} 

public class Client 
{ 
    public int NumberOfAccounts { get; set; } 
    public Decimal AmountOfPlacement { get; set; } 
    public Decimal AvgBalance { get; set; } 
    public Double NeuPlacementScore { get; set; } 
} 

Widać, że agencje zawierać listę BusinessUnits, i BusinessUnits zawierają listę klientów.

Mam też tabela mapowania nazwie BAC_Map w bazie danych, który mówi, która jest właścicielem, który i wygląda mniej więcej tak:

alt text

Jak można skonstruować zapytanie, więc mogę zapytać o i zwrócić listę agencji? Oznacza to, że chcę, aby każda agencja miała ustawioną listę obiektów BusinessUnit i chcę, aby lista BusinessObjects miała ustawioną listę klientów.

Potrafię wykonać podstawowe zapytania LINQ, ale jest to trochę ponad moją myślą dotyczącą tabeli Map i wielokrotności? zapytania.

W jaki sposób mogę skonstruować metodę taką jak GetAllAgencies(), która zapyta, nie tylko dla wszystkich agencji, ale zapełni jej BusinessUnits posiadane przez Agencję, a także dla Klientów, których właścicielem jest BusinessUnits?


Edytuj: Wszelkie wskazówki lub informacje są mile widziane. Czy muszę robić połączenia? Czy musi to być wiele zapytań, aby zwrócić listę agencji z zapełnionymi zasobami?

+4

Masz Beibered – msmucker0527

Odpowiedz

4

Jeśli zrzucisz wszystkie cztery tabele (Agencja, BusinessUnit, Klient, Mapa) na projektanta linq do sql i narysujesz relacje z mapy na pozostałe trzy, na Mapie pojawi się kilka przydatnych właściwości.

//construct a query to fetch the row/column shaped results. 
var query = 
    from m in db.map 
    //where m.... ? 
    let a = m.Agency 
    let b = m.BusinessUnit 
    let c = m.Client 
    // where something about a or b or c ? 
    select new { 
    AgencyID = a.AgencyID, 
    AgencyName = a.Name, 
    BusinessUnitID = b.BusinessUnitID, 
    ClientID = c.ClientID, 
    NumberOfAccounts = c.NumberOfAccounts, 
    Score = c.Score 
    }; 
    //hit the database 
var rawRecords = query.ToList(); 

    //shape the results further into a hierarchy.  
List<Agency> results = rawRecords 
    .GroupBy(x => x.AgencyID) 
    .Select(g => new Agency() 
    { 
    Name = g.First().AgencyName, 
    BusinessUnits = g 
    .GroupBy(y => y.BusinessUnitID) 
    .Select(g2 => new BusinessUnit() 
    { 
     Clients = g2 
     .Select(z => new Client() 
     { 
     NumberOfAccounts = z.NumberOfAccounts, 
     Score = z.Score 
     }) 
    }) 
    }) 
    .ToList(); 

Jeśli filtry approriate są dostarczane (patrz wykomentowane where klauzule), to tylko potrzebne fragmenty tabel zostanie wciągnięty do pamięci. To jest standardowe połączenie SQL w pracy tutaj.

+0

@David B, to wygląda naprawdę promosing! Ciekawi mnie, czy możesz wyjaśnić skomentowane stwierdzenia, gdzie się znajdują? Jaki powinien być stan, biorąc pod uwagę moje wymagania? – KingNestor

+0

Jeśli naprawdę potrzebujesz agencji "Wszystkie", nie powinno być filtrowania. Zwykle nie trzeba wszystkich danych w bazie danych, więc wszelkie kryteria, które można podać, ograniczą odczyt i przesyłanie rekordów. –

+0

@ David B, gotchya. Bardzo dziękuję za odpowiedź! – KingNestor

0

Jeśli robisz to z bezpośrednim LINQ do SQL, nie ma sposobu, aby to zrobić bez jakiejś rekursji, niezależnie od tego, czy robisz to sam, czy ukrywasz to za metodą rozszerzenia. Rekurencyjny SQL jest bardzo zły (wiele podróży w obie strony, wiele pojedynczych zapytań).

Dostępne są dwie opcje. Jednym z nich jest przeciągnięcie całej tabeli (ów) z hierarchią do pamięci i użycie LINQ do obiektów na nim. Pozostaw tabele "szczegóły" w SQL. Jeśli masz mniej niż kilka tysięcy podmiotów, jest to prawdopodobnie najbardziej efektywna droga. Możesz zachować jedną kopię tabel w pamięci podręcznej i odświeżyć je, jeśli to konieczne. Gdy potrzebujesz pobrać bardziej szczegółowe dane z bazy danych dla pojedynczego rekordu, możesz ponownie dołączyć tę encję z buforowanej hierarchii do nowego DataContext i pobrać ją.

Inną opcją jest użycie bardziej złożonego modelu relacji w bazie danych. Przechowywanie rodziców tylko z natury wymaga rekursji, ale można użyć modelu adjacency list model do skonstruowania pojedynczego zapytania, które może obejmować wiele poziomów dziedziczenia. Będzie to oznaczać, że zapytania LINQ do SQL stają się mniej intuicyjne (wysyłanie zapytań pod numer Entity.Right i Entity.Left nie jest tak piękne jak Parent lub Children ...), ale można zrobić w jednym zapytaniu, co może zająć setki lub tysiące w dosłownej rekurencji.

2

Stworzyłem twoje tabele w bazie danych SQL Server i próbowałem odtworzyć twój scenariusz w LinqPadzie. Skończyło się z następującymi stwierdzeniami LINQ, które wynikają zasadniczo w tej samej strukturze swoich klas POCO:

var map = from bac in BAC_Maps 
      join a in Agencies on bac.Agency_ID equals a.Agency_ID 
      join b in BusinessUnits on bac.Business_Unit_ID equals b.Business_Unit_ID 
      join c in Clients on bac.Client_ID equals c.Client_ID 
      select new 
      { 
       AgencyID  = a.Agency_ID, 
       BusinessUnitID = b.Business_Unit_ID, 
       Client   = c 
      }; 

var results = from m in map.ToList() 
       group m by m.AgencyID into g 
       select new 
       { 
        BusinessUnits = from m2 in g 
            group m2 by m2.BusinessUnitID into g2 
            select new 
            { 
             Clients = from m3 in g2 
               select m3.Client 
            } 
       }; 

results.Dump(); 

Zauważ, że nazwałem map.ToList() w drugim zapytaniu. W rezultacie uzyskano jedno, wydajne zapytanie. Moja początkowa próba nie obejmowała metody .ToList() i zaowocowała dziewięcioma osobnymi zapytaniami, które dały takie same wyniki. Zapytania generowane przez .ToList() wersja jest następująca:

SELECT [t1].[Agency_ID] AS [AgencyID], [t2].[Business_Unit_ID] AS [BusinessUnitID], [t3].[Client_ID], [t3].[NumberOfAccounts], [t3].[AmountOfPlacement], [t3].[AvgBalance], [t3].[NeuPlacementScore] 
FROM [BAC_Map] AS [t0] 
INNER JOIN [Agencies] AS [t1] ON [t0].[Agency_ID] = [t1].[Agency_ID] 
INNER JOIN [BusinessUnits] AS [t2] ON [t0].[Business_Unit_ID] = [t2].[Business_Unit_ID] 
INNER JOIN [Clients] AS [t3] ON [t0].[Client_ID] = [t3].[Client_ID] 

Oto zrzut ekranu z wynikami:

alt text http://img411.imageshack.us/img411/5003/agencybusinessunitclien.png

Powiązane problemy