使用LoadWith“深度加载”

时间:2010-08-20 10:01:08

标签: c# sql linq

我有一个表结构,每个表都有一个名为“ID”的主键字段和与其父表的主键匹配的外键名称。因此,下表中的主键出现在另一个表中,而任何表中的第一个字段都是主键:

Category
--------
CategoryID
Title


CategoryList
------------
CategoryListID
CategoryID
ListID


List
----
ListID
Title


DataPoint
---------
DataPointID
RecordedDateTime


DataPointValue
--------------
DataPointValueID
DataPointID
TheValue

以上是Category和List之间通过CategoryList进行的多对多连接。它也是从List到DataPoint,DataPoint到DataPointValue的一对多连接。

使用C#/ LINQ并给出CategoryID值列表,我想检索:

附加到类别I的所有列表条目都有ID。使用这些List条目,我想按照RecordedDateTime Descending的顺序获取最新的1个DataPoint。从那里我想检索附加到DataPoint的每个DataPointValue。

我拥有的LINQ是:

DBDataContext context = new DBDataContext(ConnectionString);

        context.LoadOptions = new DataLoadOptions();
        context.LoadOptions.LoadWith<DataPoint>(p => p.DataPoints.OrderByDescending(p.RecordedDataTime).FirstOrDefault());

        // this next line is how I get the list of category IDs, but don't worry about that...
        List<int> categoryIDs = (from TreeNode n in nodes
                                 select Int32.Parse(n.Value)).Distinct().ToList();

        var lists = from i in context.List
                                         join ci in context.CategoryLists on i.ListID equals ci.ListID
                                         join p in context.DataPoints on i.ListID equals p.ListID
                                         join v in context.DataPointValues on p.DataPointID equals v.DataPointID
                                         where categoryIDs.Contains(ci.CategoryID)
                                         orderby i.Title ascending
                                         select new
                                         {
                                             List = i,
                                             DataPoint = p,
                                             DataPointValues = p.DataPointValues
                                         };

但这显然不起作用 - LoadWith引起了我的问题。有人可以解释如何构造LoadWith,以便它可以导致尽可能少的SQL查询来检索这个(确实很大)数据量吗?

非常感谢,

1 个答案:

答案 0 :(得分:3)

你在一个月前问过这个问题,但无论如何这里都是答案......

这里有一些问题:

  • 在上下文中设置LoadOptions属性后,您无法更改它。您应该创建和配置DataLoadOptions对象,完成后,将其分配给上下文。

  • LoadWith指定自动加载父项的子项。因此loadOptions.LoadWith<DataPoint>(p => p.DataPointValues)将自动加载DataPoint的子项,而不是等到访问DataPoint.DataPointValues(或任何您命名的)属性。 LoadWith使加载非懒惰(急切)。

  • AssociateWith允许您过滤和订购自动加载关系的子项。例如,loadOptions.AssociateWith<DataPoint>(p => p.DataPointValues.OrderByDescending(v => v.TheValue))将按值对DataPointValues进行排序。

最后,我可能会将您的查询分解为两个,只是为了让它变得更容易。

// first setup a DataPoint -> DataPointValue relationship in your DBML
// then set up the DataPointValues to automatically load with DataPoint:

dataLoadOptions.LoadWith<DataPoint>(dp => dp.DataPointValues);

// then assign the load options to the context here

// First query
List<int> listIDs = context.CategoryLists
        .Where(cl => categoryIDs.Contains(cl.CategoryListID))
        .Select(cl => cl.ListID)
        .ToList();


// Second query(ies) - this isn't the most elegant, but simple is usually better :)
List<DataPoint> dataPoints = new List<DataPoint>();

foreach (int listID in listIDs)
{
    DataPoint foundDP = context.DataPoints
        .Where(dp => listIDs.Contains(dp.ListID))
        .OrderByDescending(dp => dp.RecordedDateTime)
        .Take(1)
        .SingleOrDefault();

        // Remember, at this point DataPointValues will already be loaded into the DataPoint

    if (foundDP != null)
        dataPoints.Add(foundDP);
}

无论如何,这是一个很长的答案,你甚至可能需要甚至不需要!好吧,我猜这是我的习惯。希望它有所帮助。

修改

抱歉,开始考虑这个......

你可能会这样做(更清洁,更快):

loadOptions.LoadWith<List>(l => l.DataPoints);
loadOptions.AssociateWith<List>(l => l.DataPoints.OrderByDescending(dp => dp.RecordedDateTime).Take(1));
loadOptions.LoadWith<DataPoint>(dp => dp.DataPointValues);

// assign the LoadOptions here,     
// then:

List<DataPoint> dataPoints = context.CategoryLists
        .Where(cl => categoryIDs.Contains(cl.CategoryID))
        .Select(cl => cl.List.DataPoints)
        .ToList();