使用外部列表来命令linq查询

问题描述 投票:1回答:3
var results = (from bk in bookContext.Books
           orderby bk.id descending
           select bk).ToList();

表“Book”也具有类型id,并且如果用户订阅特定类型,则在列出建议书籍时,我想首先按那些订阅类型排序,然后按书籍ID排序。我可以在下面的列表中获得订阅的流派ID列表。

List<int> subGenereIds = new List<int>() { 10, 12, 22 };

如何将按部分的顺序添加到上面的初始Linq查询中,以便用户首先获得列表中具有流派id的书籍,之后的其余部分?

c# asp.net .net entity-framework linq
3个回答
1
投票

您需要在内存中进行排序,因为没有办法将该排序逻辑传递给SQL。您可以通过使用AsEnumerable()将上下文从SQL查询更改为内存中查询来实现:

 var results = bookContext.Books
                          .AsEnumerable()
                          .OrderByDescending(bk => bk.Id)
                          .ThenBy(bk => subGenereIds.IndexOf(bk.Id));

0
投票

从您的评论中看起来您需要尽可能多地在数据库中完成此操作。假设您将书籍和用户订阅存储在同一个位置,您可以将用户首选项的查询与书籍表的搜索结合起来,以获得您所追求的结果。

为了论证,让我们假设你有一些看起来像POCO的表:

[Table("Books")]
public class Book
{
    public int ID { get; set; }
    public int Genre { get; set; }
    public string Name { get; set; }
    public string Author { get; set; }
    public Date PublishDate { get; set; }
}

[Table("Genres")]
public class Genre
{
    public int ID { get; set; }
    public string Name { get; set; }
}

[Table("Users")]
public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
}

[Table("UserGenrePreferences")]
public class UserGenrePreference
{
    public int User { get; set; }
    public int Genre { get; set; }
}

为了示例,我们在每本书中都有一种类型,并且在UserGenrePreferences表中没有重复。

基本排序可能如下所示:

// get genre preferences for the selected user
var userPrefs =
    from pref in context.UserGenrePReferences
    where pref.User == userID
    select new { Genre = (int?)pref.Genre };

var result = 
    from book in context.Books
    join pref in userPrefs on book.Genre equals pref.Genre
    into prefTemp
    from pref in prefTemp.DefaultIfEmpty()
    orderby (pref.Genre == null ? 1 : 0), book.ID
    select book;

这会针对用户的类型首选项进行外部联接。具有匹配类型的书籍在匹配的pref记录中将具有非空值,因此它们将被放置在结果中不匹配的书籍之前。然后通过正常的.Skip(...).Take(...)方法处理分页。

对于非常大的数据集,这仍然需要一些时间来运行。如果您期望用户将通过数据进行分页,那么抓住书籍ID并将其缓存在内存中可能是个好主意。获取前几千个结果,并在需要时寻找更多结果。然后在用户需要时抓取书籍记录,这比在每个页面上运行上述查询要快得多。


另一方面,如果您的用户首选项数据不在同一个数据库中,您可以使用Contains方法进行排序。

假设您在内存中只有一个类型ID数组,那么您应该能够这样做:

int[] userPrefs = new int[] { 1, 2, 3, 5 };

var result = 
    from book in context.Books
    orderby (userPrefs.Contains(book.Genre) ? 0 : 1), book.ID
    select book;

根据ORM,这将转化为类似于:

SELECT *
FROM Books
ORDER BY
    CASE WHEN Genre = 1 OR Genre = 2 OR Genre = 3 OR Genre = 5 THEN 0 ELSE 1 END,
    ID

这可能相当快,但如果首选项列表非常大,则可能会更慢。


0
投票

我不建议您编写更复杂的linq查询来解决它。

是的,它变得越来越短,但也越来越难以阅读。下面的答案越长。你可以自己缩短它。

首先,您应该获得具有subGenereIds列表的genreIds的那些。

var onesWithGenreIdFromList = (from bk in bookContext.Books
where subGenereIds.Contains(bk.genreId).orderby bk.Id descending select bk).ToList();

然后从列表中获取列表的其余部分(那些没有subGenereIds列表的genreId)。

var results = (from bk in bookContext.Books
where !subGenereIds.Contains(bk.genreId)
orderby bk.id descending
select bk).ToList();

作为最后一步,您应该将第一个列表添加到第二个列表以创建您想要的订单:

onesWithGenreIdFromList.AddRange(results);
var lastResults = onesWithGenreIdFromList;

快乐的编码!

© www.soinside.com 2019 - 2024. All rights reserved.