2009-06-24 11 views
5

Tengo 3 tipos de objetos: Agencia, BusinessUnit y Cliente (cada uno con su propia tabla)¿Cómo puedo consultar esta información jerárquica utilizando LINQ?

En términos de jerarquía, las agencias poseen BusinessUnits y BusinessUnits poseen clientes.

tengo 3 Objetos C# POCO para representarlos (por lo general selecciona nueva {} en ellos, en lugar de utilizar el LINQ genera clases):

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

se puede ver que los organismos contienen una lista de BusinessUnits, y BusinessUnits contienen una lista de clientes.

también tengo una tabla de asignación de llamada BAC_Map en la base de datos que dice que es propietaria de la cual, y se ve algo como esto:

alt text

¿Cómo puedo construir una consulta, para que pueda consultar los e devolver una lista de agencias? Lo que significa que quiero que cada Agencia tenga su lista de objetos BusinessUnit configurados, y quiero que la lista de BusinessObjects tenga su lista de Clientes configurados.

Puedo hacer consultas básicas de LINQ, pero esto es un poco sobre mi cabeza con respecto a la tabla de mapas y el múltiplo? consultas.

¿Cómo podría construir un método como GetAllAgencies() que podría consultar, no solo para todas las agencias, sino para poblar sus BusinessUnits propiedad de Agency, y los clientes de esas BusinessUnits?


Editar: Cualquier consejo o información se aprecia. ¿Debo hacer uniones? ¿Es necesario que se realicen múltiples consultas para devolver una lista de la Agencia, con sus submenúes llenos?

+4

Tienes Beibered – msmucker0527

Respuesta

4

Si suelta las cuatro tablas (Agencia, BusinessUnit, Cliente, Mapa) en el diseñador de linq a sql, y dibuja relaciones de Mapa a las otras tres, habrá algunas propiedades útiles en Mapa.

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

Si los filtros se suministran approriate (ver el comentado where cláusulas), a continuación, sólo las partes necesarias de las tablas se detuvieron en la memoria. Esto es unir SQL estándar en el trabajo aquí.

+0

@ David B, este se ve muy promosing! Tengo curiosidad, ¿pueden dar más detalles sobre las declaraciones comentadas donde? ¿Qué condición debería tener, teniendo en cuenta mis requisitos, ¿no crees? – KingNestor

+0

Si realmente necesita "Todas" las agencias, entonces no debería haber filtrado. Por lo general, uno no necesita todos los datos en la base de datos, por lo que cualquier criterio que pueda proporcionar limitará los registros leídos y transferidos. –

+0

@David B, gotchya. Aprecio mucho tu respuesta! – KingNestor

0

Si está haciendo esto con LINQ directo a SQL, no hay forma de hacerlo sin algún tipo de recursión, ya sea que lo haga usted mismo o lo oculte detrás de un método de extensión. El SQL recursivo es muy malo (muchos viajes redondos, muchas consultas únicas).

Aquí hay dos opciones. Una es tirar la (s) tabla (s) completa (s) con la jerarquía a la memoria y usar LINQ para Objetos en ella. Deje las tablas de "detalles" en SQL. Si tiene menos de varios miles de entidades, esta es probablemente la forma más eficiente de hacerlo. Puede mantener una sola copia de la (s) tabla (s) en la memoria caché y actualizarlas cuando sea necesario. Cuando necesite obtener datos más detallados de la BD para un solo registro, puede volver a conectar esa entidad desde su jerarquía en caché a un nuevo DataContext y buscarla.

La otra opción es utilizar un modelo de relación más complejo en su base de datos. Almacenar el padre solo por naturaleza requiere recursión, pero puede usar el adjacency list model para construir una única consulta que puede abarcar muchos niveles de herencia. Esto significará que sus consultas de LINQ a SQL serán menos intuitivas (consultar contra Entity.Right y Entity.Left no es tan bonito como Parent o Children ...) pero puede hacer en una consulta lo que podría llevar cientos o miles en el enfoque literal recursivo.

2

Creé sus tablas en una base de datos SQL Server e intenté recrear su escenario en LinqPad. Terminé con las siguientes afirmaciones LINQ, que básicamente resultan en la misma estructura de sus clases 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(); 

Tenga en cuenta que llamé map.ToList() en la segunda consulta. Esto resultó en una consulta única y eficiente. Mi intento inicial no incluyó .ToList(), y generó nueve consultas separadas para producir los mismos resultados. La consulta generada por el .ToList() versión es la siguiente:

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] 

Aquí está una captura de pantalla de los resultados:

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

Cuestiones relacionadas