C# 查询嵌套数据的有效方法

C# 查询嵌套数据的有效方法,c#,.net,sql-server,sql-server-2008,C#,.net,Sql Server,Sql Server 2008,我需要从一个表中选择一些“主”行,同时为每个结果返回另一个表中的一些详细行。不需要多个查询(一个用于主行,一个用于每个结果以获取详细行)就可以实现这一点的好方法是什么 例如,使用如下数据库结构: MasterTable: - MasterId BIGINT - Name NVARCHAR(100) DetailTable: - DetailId BIGINT - MasterId BIGINT - Amount MONEY 如何最有效地填充下面的数据对

我需要从一个表中选择一些“主”行,同时为每个结果返回另一个表中的一些详细行。不需要多个查询(一个用于主行,一个用于每个结果以获取详细行)就可以实现这一点的好方法是什么

例如,使用如下数据库结构:

MasterTable:
    - MasterId BIGINT
    - Name NVARCHAR(100)

DetailTable:
    - DetailId BIGINT
    - MasterId BIGINT
    - Amount MONEY
如何最有效地填充下面的
数据
对象

IList<MasterDetail> data;

public class Master
{
    private readonly List<Detail> _details = new List<Detail>();

    public long MasterId
    {
        get; set;
    }

    public string Name
    {
        get; set;
    }

    public IList<Detail> Details
    {
        get
        {
            return _details;
        }
    }
}

public class Detail
{
    public long DetailId
    {
        get; set;
    }

    public decimal Amount
    {
        get; set;
    }
}
IList数据;
公共班主任
{
私有只读列表_details=new List();
公共长MasterId
{
获得;设置;
}
公共字符串名
{
获得;设置;
}
公共IList详细信息
{
得到
{
返回详细信息;
}
}
}
公共类详细信息
{
公共长细节ID
{
获得;设置;
}
公共小数金额
{
获得;设置;
}
}

从主菜单中选择


从master M中选择,在M.Id=C.MasterID上连接子C

您可以通过两次查询和对每个结果集进行一次传递来完成此操作:


查询按MasterId排序的所有母版,然后查询也按MasterId排序的所有详细信息。然后,使用两个嵌套循环,迭代主数据并为主循环中的每一行创建一个新的主对象,并在细节与当前主对象具有相同的主ID时迭代细节,并在嵌套循环中填充其_details集合。

根据数据集的大小,您可以通过两个查询(一个用于所有主对象,一个用于所有嵌套数据)将所有数据拉入内存中的应用程序中然后使用它以编程方式为每个对象创建子列表,例如:

List<Master> allMasters = GetAllMasters();
List<Detail> allDetail = getAllDetail();

foreach (Master m in allMasters)
    m.Details.Add(allDetail.FindAll(delegate (Detail d) { return d.MasterId==m.MasterId });
List allMasters=GetAllMasters();
List allDetail=getAllDetail();
foreach(所有大师中的大师m)
m、 Add(allDetail.FindAll(委托(细节d){return d.MasterId==m.MasterId});

使用这种方法,您基本上是在用内存占用换取速度。您可以很容易地进行调整,以便
GetAllMasters
GetAllDetail
只返回您感兴趣的主项和细节项。另外请注意,要使其有效,您需要将MasterId添加到细节类中。通常,我会选择这两个网格方法—不过,您可能还希望查看XML—将父/子数据格式化为XML并从中加载是相当容易的(在SQL Server 2005及更高版本中)

SELECT parent.*,
       (SELECT * FROM child
       WHERE child.parentid = parent.id FOR XML PATH('child'), TYPE)
FROM parent
FOR XML PATH('parent')
另外-LINQ to SQL支持这种类型的模型,但您需要提前告诉它您需要哪些数据。通过:

//来自MSDN的示例
Northwnd db=新的Northwnd(@“c:\Northwnd.mdf”);
DataLoadOptions dlo=新的DataLoadOptions();
dlo.LoadWith(c=>c.Orders);
db.LoadOptions=dlo;
var伦敦客户=
来自数据库中的客户
其中客户城市==“伦敦”
选择客户;
foreach(伦敦客户的var custObj)
{
Console.WriteLine(custombj.CustomerID);
}

如果您不使用
LoadWith
,您将得到n+1个查询-一个主列表,每个主列表行一个子列表。

可以通过以下单个查询完成:

select   MasterTable.MasterId,
         MasterTable.Name,
         DetailTable.DetailId,
         DetailTable.Amount
from     MasterTable
         inner join
         DetailTable
         on MasterTable.MasterId = DetailTable.MasterId
order by MasterTable.MasterId
然后在psuedo代码中

foreach(row in result)
{
   if (row.MasterId != currentMaster.MasterId)
   {
       list.Add(currentMaster);
       currentMaster = new Master { MasterId = row.MasterId, Name = row.Name };
   }
   currentMaster.Details.Add(new Detail { DetailId = row.DetailId, Amount = row.Amount});
}
list.Add(currentMaster);

<> P>有几个边可以剔除它,但它应该给你大概的想法。

< P>这是一个你可以考虑的替代方案。每个开发者花费150美元,但是时间也是金钱……/P> 我们使用一个名为的对象持久化层,它为您生成代码,让您完全按照自己的意愿执行操作,并且您可以在架构发生更改时重新生成。用数据填充对象是透明的。使用上面描述的对象看起来是这样的(请原谅我的VB,但它也在C#中工作):


这取决于主表的宽度……如果主表有很多列(或一些非常胖的列,如nvarchar(max)等),那么您就不想一遍又一遍地复制主数据。如果主数据只有几个窄列,您可能会冒风险。是的,这通常是一个需要考虑的问题,但问题是如何在没有多个查询的情况下执行此操作,所以代码就是这样实现的
foreach(row in result)
{
   if (row.MasterId != currentMaster.MasterId)
   {
       list.Add(currentMaster);
       currentMaster = new Master { MasterId = row.MasterId, Name = row.Name };
   }
   currentMaster.Details.Add(new Detail { DetailId = row.DetailId, Amount = row.Amount});
}
list.Add(currentMaster);
Dim master as New BusinessObjects.Master
master.LoadByPrimaryKey(43)
Console.PrintLine(master.Name)
For Each detail as BusinessObjects.Detail in master.DetailCollectionByMasterId
   Console.PrintLine(detail.Amount)
   detail.Amount *= 1.15
End For
With master.DetailCollectionByMasterId.AddNew
   .Amount = 13
End With
master.Save()