Использование внешнего списка для заказа запроса linq - PullRequest
0 голосов
/ 31 августа 2018
var results = (from bk in bookContext.Books
           orderby bk.id descending
           select bk).ToList();

Таблица «Книга» также имеет идентификатор жанра, и если пользователь подписывается на определенный жанр, при перечислении предлагаемых книг я хочу сначала заказать эти подписанные жанры, а затем - по идентификатору книги. Я могу получить список идентификаторов подписанных жанров в виде списка ниже.

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

Как добавить порядок по разделам в исходный запрос Linq выше, чтобы пользователи получали книги с идентификатором жанра в списке вначале, а остальные - после?

Ответы [ 3 ]

0 голосов
/ 31 августа 2018

Вам понадобится упорядочить в памяти, так как нет способа передать эту логику упорядочения в SQL. Это можно сделать с помощью AsEnumerable(), чтобы изменить контекст запроса SQL на запрос в памяти:

 var results = bookContext.Books
                          .AsEnumerable()
                          .OrderByDescending(bk => bk.Id)
                          .ThenBy(bk => subGenereIds.IndexOf(bk.Id));
0 голосов
/ 31 августа 2018

Судя по вашим комментариям, вам нужно сделать как можно больше этого в базе данных. Предполагая, что книги и пользовательские подписки хранятся в одном и том же месте, вы можете объединить запрос предпочтений пользователя с поиском по таблице книг, чтобы получить нужный результат.

Ради аргумента предположим, что у вас есть несколько таблиц, которые выглядят примерно так, как 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(...) методом.

Для очень больших наборов данных это все еще займет некоторое время для запуска. Если вы ожидаете, что пользователь будет перелистывать данные, было бы неплохо получить только идентификаторы книг и кэшировать их в памяти. Возьмите первые несколько тысяч результатов и, например, ищите больше при необходимости. Затем возьмите записи книги, когда пользователь захочет их получить, что будет намного быстрее, чем выполнение запроса выше для каждой страницы.


Если, с другой стороны, ваши данные о пользовательских предпочтениях не находятся в той же базе данных, вы можете использовать метод Contains для выполнения заказа.

Предполагая, что у вас в памяти есть массив идентификаторов жанров, вы можете сделать это:

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 голосов
/ 31 августа 2018

Я не рекомендую вам писать более сложный запрос linq для его решения.

Да, так становится короче, но становится все труднее читать. Более длинный ответ ниже. Вы можете сократить его самостоятельно.

Сначала вы должны получить те, которые имеют жанры из списка subGenereIds.

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

Затем получите остальную часть списка (те, которые не имеют genreId списка subGenereIds) из вашего списка.

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;

Удачного кодирования!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...