#.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 в том порядке, который вам нравится, и присоединиться к нему, чтобы получить клиентов в том же порядке. Это будет один запрос. Дополнительный код там для тестирования.