2009-06-24 60 views
5

我有3种对象:代理商,商业单位和客户(每个都有自己的表)如何使用LINQ查询此分层数据?

根据层次结构,代理商拥有BusinessUnits和BusinessUnits自己的客户。

我有3个C#POCO对象来表示他们(我通常选择新的{}放进去,而不是使用LINQ生成的类):

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

你可以看到,机构包含BusinessUnits的列表,而BusinessUnits包含一个客户列表。

我也有它说拥有它的数据库名为BAC_Map的映射表,它看起来是这样的:

alt text

我如何构建一个查询,这样我就可以查询和返回机构列表?这意味着,我希望每个代理商都设置其BusinessUnit对象列表,并且我希望BusinessObjects列表设置其客户端列表。

我可以做基本的LINQ查询,但是这对我的头有点关于地图表和多个?查询。

我该如何构建一个像GetAllAgencies()这样的方法,它不仅可以查询所有代理商,还可以填充代理商拥有的BusinessUnits以及这些BusinessUnits拥有的客户端?


编辑:任何提示或信息表示赞赏。我需要做连接吗?这是否需要多个查询才能返回代理列表,其子代表已填充?

+4

你有Beibered – msmucker0527 2013-06-28 16:11:47

回答

4

如果您将linq上的所有四个表(Agency,BusinessUnit,Client,Map)放到sql设计器中,并且将Map中的关系绘制到其他三个表中,则Map上会有一些有用的属性。

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

如果提供approriate滤波器(见注释where条款),则仅表的所需要的部分会被拉入内存。这是在这里工作的标准SQL连接。

+0

@大卫B,这看起来真的promosing!我很好奇,你能否详细说明哪些声明在哪里?考虑到我的要求,您应该考虑什么样的条件? – KingNestor 2009-06-25 01:35:43

0

如果你是直接LINQ to SQL来做这件事,没有某种递归方式就无法做到这一点,不管你自己做,还是把它隐藏在扩展方法的后面。递归SQL非常糟糕(很多往返,很多单个查询)。

这里有两个选项。一种方法是将整个表与层次结构拉到内存中,并使用LINQ到对象。在SQL中留下“细节”表。如果您拥有不到几千个实体,这可能是最有效的方法。您可以将一个或多个表的副本保存在缓存中,并在必要时进行刷新。当您需要从数据库中为单个记录提取更详细的数据时,您可以将该实体从缓存层次结构重新附加到新的DataContext并获取它。

另一种选择是在数据库中使用更复杂的关系模型。仅按性质存储父级要求递归,但您可以使用adjacency list model构造一个可跨越多级继承的单个查询。这将意味着您的LINQ to SQL查询变得不那么直观(查询Entity.RightEntity.Left并不像ParentChildren ...),但是您可以在一个查询中执行字面递归方法中可能需要数百或数千的查询。

2

我在SQL Server数据库中创建了你的表,并试图在LinqPad中重新创建你的场景。我结束了以下LINQ语句,基本上导致您的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(); 

注意,我在第二个查询称为map.ToList()。这实际上导致了一个单一的高效查询。我最初的尝试不包括.ToList(),并导致九个单独的查询产生相同的结果。由.ToList生成的查询()版本如下:

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] 

下面是结果的截图:

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