OrderBy与实体Sql的多对多关系

本文关键字:关系 Sql 实体 OrderBy | 更新日期: 2023-09-27 18:26:39

在以下场景中,我试图更好地利用实体Sql的资源:我有一个表Book,它与Author表具有多对多关系。每本书可能有0到N位作者。我想按第一作者的名字对书进行排序,即在这种关系中找到的第一条记录(或者当没有作者链接到一本书时为null)。

使用T-SQL可以毫不费力地完成:

SELECT
    b.*
FROM
    Book AS b
    JOIN BookAuthor AS ba ON b.BookId = ba.BookId
    JOIN Author AS a ON ba.AuthorId = a.AuthorId
ORDER BY
    a.AuthorName;

但我想不出如何调整下面的代码来实现它。事实上,我也不知道如何直接用实体Sql编写等效的东西。

Entities e = new Entities();
var books = e.Books;
var query = books.Include("Authors");
if (sorting == null)
    query = query.OrderBy("it.Title asc");
else
    query = query.OrderBy("it.Authors.Name asc"); // This isn't it.
return query.Skip(paging.Skip).Take(paging.Take).ToList();

有人能解释一下如何修改我的代码以生成所需结果的实体Sql吗?或者甚至向我解释如何使用CreateQuery<Book>()手工编写查询来实现它?

编辑

为了说明问题,我将收集大量书籍(约10万册)。在记忆中对它们进行排序会对性能产生很大影响。我希望答案集中在如何使用Entity Sql生成所需的排序上,,这样排序就会发生在数据库上。

OrderBy与实体Sql的多对多关系

OrderBy方法希望您给它一个lambda表达式(实际上是Func委托,但大多数人会使用lambda来生成它们),该表达式可以运行来选择要排序的字段。此外,OrderBy总是升序;如果你想要降序,有一个OrderByDescending方法。

var query = books
  .Include("Authors")
  .OrderBy(book => book.Authors.Any()
    ? book.Authors.FirstOrDefault().Name
    : string.Empty);

这基本上是告诉OrderBy方法:"对于序列中的每本书,如果有任何作者,请选择第一本的名字作为我的排序键;否则,请选择空字符串。然后返回按排序键排序的书籍。"

您可以将任何内容放在string.Empty的位置,包括例如book.Title或用于代替姓氏进行排序的书籍的任何其他属性。

根据评论编辑:

只要您要求的排序行为不太复杂,实体框架的查询提供程序通常可以找出如何将其转换为SQL。它会非常非常努力地做到这一点,如果做不到,就会出现查询错误。在客户端对象中进行排序的唯一时间是在调用OrderBy之前强制运行查询(例如.AsEnumerable())。

在这种情况下,EF输出一个选择语句,其中包括以下计算字段:

    CASE WHEN ( EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[BookAuthor] AS [Extent4]
        WHERE [Extent1].[Id] = [Extent4].[Books_Id]
    )) THEN [Limit1].[Name] ELSE @p__linq__0 END AS [C1], 

然后按此命令。

@p__linq__0是一个参数,作为string.Empty传入,因此您可以看到它非常直接地将lambda表达式转换为SQL。Extent和Limit只是为联接表等生成的SQL中使用的别名。Extent1[Books]Limit1是:

SELECT TOP (1) -- Field list goes here.
FROM  [dbo].[BookAuthor] AS [Extent2]
INNER JOIN [dbo].[Authors] AS [Extent3] ON [Extent3].[Id] = [Extent2].[Authors_Id]
WHERE [Extent1].[Id] = [Extent2].[Books_Id] 

如果您不关心排序发生在哪里(即SQL与In Code),您可以检索结果集,并在返回查询结果后使用自己的排序代码对其进行排序。根据我的经验,使用实体框架进行这样的专业排序可能非常困难、令人沮丧和耗时。