Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用LINQ查询此分层数据?_C#_Linq_Linq To Sql - Fatal编程技术网

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

C# 如何使用LINQ查询此分层数据?,c#,linq,linq-to-sql,C#,Linq,Linq To Sql,我有三种对象:代理、业务单位和客户,每种都有各自的表格 就层级而言,机构拥有业务单位,业务单位拥有客户 我有3个C POCO对象来表示它们,我通常在其中选择新的{},而不是使用LINQ生成的类: public class Agency { public IEnumerable<BusinessUnit> BusinessUnits { get; set; } } public class BusinessUnit { public IEnumerable<Cl

我有三种对象:代理、业务单位和客户,每种都有各自的表格

就层级而言,机构拥有业务单位,业务单位拥有客户

我有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; }
}
您可以看到代理机构包含业务单位列表,业务单位包含客户列表

我在数据库中还有一个名为BAC_Map的映射表,上面写着谁拥有谁,它看起来像这样:

如何构造查询,以便查询并返回代理列表?也就是说,我希望每个机构都设置其BusinessUnit对象列表,我希望BusinessObjects列表设置其客户列表

我可以进行基本的LINQ查询,但是关于映射表和多重查询,这有点超出了我的理解范围?查询

我如何构建一个像GetAllAgencies这样的方法,它不仅可以查询所有代理,还可以填充代理所拥有的业务单元以及这些业务单元所拥有的客户


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

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

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


另一种选择是在数据库中使用更复杂的关系模型。仅按性质存储父级需要递归,但您可以使用构造一个可以跨越多个继承级别的查询。这将意味着您的LINQ到SQL查询对实体的查询变得不那么直观。右和实体。左不如父或子实体漂亮。。。但是,您可以在一个查询中执行文字递归方法可能需要数百或数千的操作。

如果您在linq to sql设计器上删除所有四个表Agency、BusinessUnit、Client、Map,并从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连接。

我在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]
以下是结果的屏幕截图:


@大卫B,这看起来真的很刺激!我很好奇,你能详细解释一下注释掉的where语句吗?考虑到我的要求,你认为我应该具备什么条件?如果你真的需要所有的代理,那么就不应该有过滤。通常不需要数据库中的所有数据,因此您可以提供的任何条件都会限制读取和传输的记录。@David B,gotchya。我非常感谢你的回答@David B,当我键入查询的第一部分时:从m.Agency中的a中,我立即得到一个错误,即在源类型为Linq.Table的查询表达式的后续from子句中不允许使用“DomainModel.Agency”类型的表达式。调用Select时类型推断失败Many@David你能验证你的linq表达式语法吗?