所以你有一个名为Models 的表,其中填充了DataItems 类的对象。你有一张桌子Quotes。 DataItems和Quotes之间存在一对多关系:每个DataItem有零个或多个Quotes,每个Quote恰好属于一个DataItem,即外键@的DataItem 987654330@指的是。
此外,每个Quote 都有一个属性QuoteName。
请注意,我将您的 Data 类的标识符更改为 DataItem,因此在引用一个 DataItem 或引用 DataItems 集合时,我更容易用单数和复数名词说话。
您想以DataItem 的第一个Quote 的属性QuoteName 的升序排列您的DataItems。
我看到两个问题:
- 如果 DataItem 没有引号怎么办?
- 是否定义了“First Quote`”这个术语:如果您查看表格,您能说:“这是 Id == 4 的 DataItem 的第一个 Quote”吗?
这就是原因,通常最好使用virtual ICollection<Quote> 设计一对多关系,然后使用virtual IList<Quote>。 DataItem[3].Quotes[4] 的值未定义,因此让用户访问索引没有用处。
但是让我们假设,如果你有一个IQueryable<Quote>,你可以定义一个“第一个报价”。这可以是具有最低 Id 的报价,也可以是具有最旧日期的报价。也许如果它是最常被引用的报价。无论如何,您都可以定义一个扩展方法:
public static IOrderedQueryable<Quote> ToDefaultQuoteOrder(this IQueryable<Quote> quotes)
{
// order by quote Id:
return quotes.OrderBy(quote => quote.Id);
// or order by QuoteName:
return quotes.OrderBy(quote => quote.QuoteName);
// or a complex sort order: most mentioned quotes first,
// then order by oldest quotes first
return quotes.OrberByDescending(quote => quote.Mentions.Count())
.ThenBy(quote => quote.Date)
.ThenBy(quote => quote.Id);
}
如果你希望它被多次使用,创建一个扩展方法才有用。
既然我们已经在您的报价中定义了一个顺序,那么您可以从每个 DataItem 中获得第一个报价:
DataItem dataItem = ...
Quote firstQuote = dataItem.Quotes.ToDefaultQuoteOrder()
.FirstOrDefault();
注意:如果 dataItem 根本没有 Quotes,则不会有 firstQuote,因此您无法获取它的名称。因此,在连接 LINQ 语句时,通常最好只使用FirstOrDefault() 作为序列中的最后一个方法。
所以你的问题的答案是:
var result = _context.DataItems.Select(dataItem => new
{
DataItem = dataItem,
OrderKey = dataItem.Quotes.ToDefaultQuoteOrder()
.Select(quote => quote.QuoteName)
.FirstOrDefault(),
})
.OrderBy(selectionResult => selectionResult.OrderKey)
.Select(selectioniResult => selectionResult.Data);
扩展方法的好处在于您隐藏了引号的排序方式。如果您想更改这一点,而不是按 ID 排序,而是按最旧的报价日期,则用户不必更改。
最后一点:使用 Include 作为 Select 的快捷方式通常不是一个好主意。如果 DataItem [4] 有 1000 个 Quotes,那么它的每一个 Quote 都会有一个值为 4 的 DataItemId。发送这个值 4 超过一千次是相当浪费的。使用 Select 时,您只能传输您实际计划使用的属性:
.Select(dataItem => new
{
// Select only the data items that you plan to use:
Id = dataItem.Id,
Name = dataItem.Name,
...
Quotes = dataItem.Quotes.ToDefaultQuoteOrder().Select(quote => new
{
// again only the properties that you plan to use:
Id = quote.Id,
...
// not needed, you know the value:
// DataItemId = quote.DataItemId,
})
.ToList(),
});
在实体框架中始终使用 Select 来选择数据并仅选择您真正计划使用的属性。仅当您计划更改/更新包含的数据时才使用 include。
当然不要使用 Include,因为它可以节省您的打字时间。再说一遍:当你必须多次做某事时,为它创建一个过程:
作为扩展方法:
public static IQueryable<MyClass> ToPropertiesINeed(this IQueryable<DataItem> source)
{
return source.Select(item => new MyClass
{
Id = item.Id,
Name = item.Name,
...
Quotes = item.Quotes.ToDefaultQuoteOrder.Select(...).ToList(),
});
}
用法:
var result = var result = _context.DataItems.Where(dataItem => ...)
.ToPropertiesINeed();
Select 的好处在于您将数据库的结构与实际返回的数据分开。如果您的数据库结构发生变化,您的类的用户将不必看到这一点。