如何使用LINQ查询此分层数据?

时间:2009-06-24 22:20:28

标签: c# linq linq-to-sql

我有3种对象:Agency,BusinessUnit和Client(每个都有各自的表)

就层次结构而言,代理商拥有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; }
}

您可以看到代理商包含BusinessUnit列表,而BusinessUnits包含客户列表。

我在数据库中也有一个名为BAC_Map的映射表,它说哪个拥有哪个,它看起来像这样:

alt text

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

我可以做基本的LINQ查询,但这对于Map表和多个来说有点过头了吗?查询。

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


编辑:感谢任何提示或信息。我需要加入吗?这是否需要多个查询才能返回代理商列表,并填充其子成员?

3 个答案:

答案 0 :(得分: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();

如果提供了适当的过滤器(请参阅注释掉的where子句),那么只有表中所需的部分才会被拉入内存。这是标准的SQL加入工作。

答案 1 :(得分: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

答案 2 :(得分:0)

如果您使用直接LINQ to SQL执行此操作,则无法在没有某种递归的情况下执行此操作,无论您是自己执行此操作还是将其隐藏在扩展方法后面。递归SQL非常糟糕(许多往返,许多单个查询)。

这里有两种选择。一种是将具有层次结构的整个表拉入内存并在其上使用LINQ to Objects。在SQL中保留“details”表。如果您的实体少于几千个,这可能是最有效的方法。您可以在缓存中保留表的单个副本,并在必要时刷新它们。当您需要从数据库中获取单个记录的更详细数据时,可以将该实体从缓存层次结构重新附加到新的DataContext并获取它。

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