2009-06-24 11 views
5

Ho 3 tipi di oggetti: Agenzia, BusinessUnit e Client (ciascuno con la rispettiva tabella)Come posso interrogare questi dati gerarchici usando LINQ?

In termini di gerarchia, le agenzie possiedono BusinessUnit e BusinessUnits possiedono client.

Ho 3 C# POCO oggetti per rappresentare loro (io di solito selezionare nuovo {} in loro, piuttosto che utilizzare il LINQ generato classi):

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; } 
} 

Si può vedere che le agenzie contengono un elenco di BUSINESSUNIT, e BusinessUnits contengono un elenco di client.

Ho anche una tabella di mappatura chiamato BAC_Map nel database che dice che possiede la quale, e sembra qualcosa di simile:

alt text

Come posso creare una query, in modo da poter interrogare e restituire un elenco di agenzie? Ciò significa che voglio che ogni agenzia abbia il proprio elenco di oggetti BusinessUnit impostati e voglio che l'elenco di BusinessObjects abbia il proprio elenco di client impostati.

Posso eseguire query LINQ di base, ma questo è un po 'eccessivo rispetto alla tabella Mappa e al multiplo? interrogazioni.

Come potrei costruire un metodo come GetAllAgencies() che interrogherebbe, non solo per tutte le agenzie, ma popolerà le sue BusinessUnits di proprietà dell'Agenzia, e i Clienti di quelle BusinessUnit?


Modifica: Qualsiasi suggerimento o informazione è apprezzato. Devo fare join? Questo deve essere più query per restituire un elenco di agenzie, con i suoi sottomulti popolati?

+4

Hai Beibered – msmucker0527

risposta

4

Se si rilasciano tutte e quattro le tabelle (Agenzia, BusinessUnit, Client, Mappa) nella finestra di disegno da linq a sql e si creano relazioni da Mappa con le altre tre, sulla mappa verranno visualizzate alcune proprietà utili.

//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(); 

Se filtri approriate vengono forniti (vedere la commentata where clausole), allora solo le parti necessarie delle tabelle saranno inseriti in memoria. Questa è l'unione SQL standard al lavoro qui.

+0

@ Davide B, questo sembra davvero promosing! Sono curioso, puoi approfondire le dichiarazioni commentate? Quale condizione dovrei avere in atto, date le mie esigenze, pensi? – KingNestor

+0

Se hai davvero bisogno di agenzie "Tutte", allora non ci dovrebbero essere filtri. Di solito non è necessario tutti i dati nel database, quindi qualsiasi criterio che si possa fornire limiterà i record letti e trasferiti. –

+0

@David B, gotchya. Apprezzo molto la tua risposta! – KingNestor

0

Se si esegue questa operazione con LINQ diretto su SQL, non è possibile eseguire questa operazione senza una sorta di ricorsione, indipendentemente dal fatto che venga eseguita da soli o nascosta dietro un metodo di estensione. SQL ricorsivo è pessimo (molti round trip, molte query singole).

Ci sono due opzioni qui. Uno è quello di tirare l'intera tabella (s) con la gerarchia in memoria e utilizzare LINQ su oggetti su di esso. Lascia le tabelle "dettagli" in SQL. Se hai meno di diverse migliaia di entità, questo è probabilmente il modo più efficace per andare. È possibile conservare una singola copia della tabella o delle tabelle nella cache e aggiornarle quando necessario. Quando è necessario recuperare dati più dettagliati dal DB per un singolo record, è possibile ricollegare quell'entità dalla gerarchia memorizzata nella cache a un nuovo DataContext e recuperarla.

L'altra opzione consiste nell'utilizzare un modello di relazione più complesso nel database. La memorizzazione di genitore solo per natura richiede la ricorsione, ma è possibile utilizzare adjacency list model per creare una singola query che può estendersi su molti livelli di ereditarietà. Ciò significa che le query LINQ su SQL diventano meno intuitive (l'esecuzione di query su Entity.Right e Entity.Left non è così carina come Parent o Children ...) ma è possibile eseguire in una query ciò che potrebbe richiedere centinaia o migliaia nell'approccio ricorsivo letterale.

2

Ho creato le tabelle in un database SQL Server e ho provato a ricreare lo scenario in LinqPad. Ho finito con le seguenti affermazioni LINQ, che sostanzialmente si traducono nella stessa struttura delle classi 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(); 

Si noti che ho chiamato map.ToList() nella seconda query. Ciò ha portato a una query singola ed efficiente. Il mio tentativo iniziale non includeva .ToList() e risultava in nove query separate per produrre gli stessi risultati. La query generato dal ToList() versione è la seguente:

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] 

Ecco uno screenshot dei risultati:

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

Problemi correlati