Запрос Entity Framework — получение объектов в определенном порядке

#.net #linq #entity-framework

#.net #linq #entity-framework

Вопрос:

У меня есть список, в котором указаны идентификаторы ряда объектов в моей базе данных. Я хочу получить набор объектов, все из одной таблицы, которые имеют эти идентификаторы, и сохранить их в этом точном порядке, и я хочу, чтобы он выполнялся как один запрос к базе данных (не ‘N’ запросов).

Например, у меня есть список идентификаторов {5, 3, 6, 9}, и я хочу вернуть список объектов Customer с этими идентификаторами и сохранить их в порядке { Customer (5, ‘Bob’), Customer (3, ‘JimBo’), Customer (6, ‘Joe’), Customer (9, ‘Jack’) }.

Объем данных достаточно мал, поэтому я не возражаю против повторной сортировки их после запроса к БД. Я мог бы сделать все это примерно в 15 строках чистого кода (включая повторную сортировку вручную), но я чувствую, что должен быть одно- или двухстрочный запрос LINQ к EF, который должен сделать это легко.

Ответ №1:

Я полагаю, вам нужно будет выполнить упорядочение после получения результатов. Я бы, вероятно, решил это следующим образом.

 var efQuery = /* your query here */.AsEnumerable(); // force evaluation 
var orderedResult = from id in idList // { 5, 3, 6, 9 }
                    join item in efQuery
                    on id equals item.Id
                    select item;
  

Объединение списка идентификаторов в памяти с результатом запроса сохранит порядок идентификаторов.

Редактировать: Из комментария

мои паучьи чувства покалывают при использовании Join, чтобы полагаться на поддержание отсортированного порядка. Интересно, указано ли это свойство Join в документации (если нет, то оно может измениться в какой-нибудь будущей версии .NET, например, по соображениям производительности).

Я указываю вам на http://msdn.microsoft.com/en-us/library/bb534675.aspx , раздел Примечания

Соединение сохраняет порядок элементов outer и для каждого из этих элементов порядок совпадающих элементов inner.

Комментарии:

1. Спасибо. Оказывается, что AsEnumerable не является необходимым (я проверил с помощью SQL Profiler), хотя я также думал, что это будет. Я должен сказать … мои паучьи чувства покалывают при использовании Join, чтобы полагаться на поддержание отсортированного порядка. Интересно, указано ли это свойство Join в документации (если нет, то оно может измениться в какой-нибудь будущей версии .NET, например, по соображениям производительности).

2. @pbarranis, будь спокоен. Я обновил ответ, чтобы обратиться к документации по заказу.

Ответ №2:

Я не думаю, что все это можно сделать в одном запросе, но это легко сделать в двух запросах. Один в базе данных и один в памяти.

Первый запрос выберет только клиентов с указанными идентификаторами:

 var customers = (from c in context.Customers
                where itemIds.Contains(c.Id)
                select c).AsEnumerable();
  

Второй упорядочит их в соответствии с вашим списком:

 var customers = from id in itemIds 
                join c in customers 
                on id equals c.Id
                select c;

var customersList = customers.ToList();
  

Комментарии:

1. Спасибо. Оказывается, что AsEnumerable не является необходимым (я проверил с помощью SQL Profiler), хотя я также думал, что это будет. Я должен сказать … мои паучьи чувства покалывают при использовании Join, чтобы полагаться на поддержание отсортированного порядка. Интересно, указано ли это свойство Join в документации (если нет, то оно может измениться в какой-нибудь будущей версии .NET, например, по соображениям производительности).

Ответ №3:

Очевидно, что вы можете достаточно легко получить список объектов, в списке идентификаторов которых содержится искомый идентификатор. Что касается orderby, я не могу придумать, как сделать это на основе списка идентификаторов в одном запросе. Однако, если у вас есть логический способ указать, как были упорядочены исходные идентификаторы (если не случайным образом), тогда вы можете создать функцию сравнения равенства, как показано здесь

Ответ №4:

 void Main()
{
    List<Data> data = new List<Data>();
    data.Add(new Data{Id =1, Name = "ABC1"});
    data.Add(new Data{Id =2, Name = "ABC2"});
    data.Add(new Data{Id =3, Name = "ABC3"});
    data.Add(new Data{Id =4, Name = "ABC4"});
    data.Add(new Data{Id =5, Name = "ABC5"});

    var result = from d in data
                let ids = new List<int>{3,4,5}
                where ids.Any(i=> i == d.Id)
                select d;
    result.Dump();              
}

// Define other methods and classes here
class Data
{
    public int Id{get;set;}
    public string Name{get;set;}
}
  

Идентификаторы могут быть еще одним запросом для получения идентификаторов откуда-то еще, но упорядочивайте их по своему усмотрению. Обратите внимание, что это код Linqpad, следовательно, метод .Dump().

Комментарии:

1. Не уверен, почему вы так говорите, вы можете получить идентификаторы с помощью let в том порядке, который вам нравится, и присоединиться к нему, чтобы получить клиентов в том же порядке. Это будет один запрос. Дополнительный код там для тестирования.