我有一个表结构,每个表都有一个名为“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查询来检索这个(确实很大)数据量吗?
非常感谢,
太
答案 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();