Создание кортежа в Linq Select
Я работаю с С# и .NET Framework 4.5.1, получая данные из базы данных SQL Server с Entity Framework 6.1.3.
У меня есть это:
codes = codesRepo.SearchFor(predicate)
.Select(c => new Tuple<string, byte>(c.Id, c.Flag))
.ToList();
И когда я запускаю его, я получаю это сообщение:
В LINQ поддерживаются только конструкторы без параметров и инициализаторы к объектам.
Я не знаю, как мне создать Tuple, потому что все примеры, которые я нашел, в основном похожи на этот.
Я пробовал это:
codes = codesRepo.SearchFor(predicate)
.Select(c => Tuple.Create(c.Id, c.Flag))
.ToList();
И получите эту ошибку:
LINQ to Entities не распознает метод "System.Tuple`2 [System.String, System.Byte] Создайте метод [String, Byte] (System.String, Byte) и этот метод не может быть переведено в выражение хранилища.
Где проблема?
Ответы
Ответ 1
Хотя ответ от octavioccl работает, лучше сначала спроецировать результат запроса на анонимный тип, а затем переключиться на перечисляемый и преобразовать его в кортеж. Таким образом, ваш запрос будет извлекать из базы данных только необходимые поля.
codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => new Tuple<string, byte>(c.Id, c.Flag))
.ToList();
Примечание. Приведенное выше правило относится к EF6. EF Core естественным образом поддерживает кортежи (в проекции или в виде ключей соединения/группировки) через конструктор кортежей, например, оригинальный запрос просто работает
codes = codesRepo.SearchFor(predicate)
.Select(c => new Tuple<string, byte>(c.Id, c.Flag))
.ToList();
но не метод Tuple.Create
(EF Core 2.x).
Ответ 2
Просто обновленный ответ для С# 7, теперь вы можете использовать более простой синтаксис для создания ValueTuples.
codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();
Вы можете даже назвать свойства кортежа сейчас:
codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag))
.ToList();
Таким образом, вместо того, чтобы использовать его как Item1 или Item2, вы можете использовать его как Id или Flag.
Ответ 3
Попробуйте следующее:
codes = codesRepo.SearchFor(predicate)
.Select(c => Tuple.Create(c.Id, c.Flag))
.ToList();
Сообщено, что это не принимает в LINQ объектам.
Другой вариант - вывести результат в память перед выбором. Если вы собираетесь это сделать, я бы порекомендовал делать всю фильтрацию до .AsEnumerable(), поскольку это означает, что вы только отталкиваете результаты, которые хотите, а не оттягиваете всю таблицу и затем фильтруете.
codes = codesRepo.SearchFor(predicate).AsEnumerable()
.Select(c => Tuple.Create(c.Id, c.Flag))
.ToList();
а также Tuple.Create(c.Id, c.Flag) может быть изменен на новый Tuple (c.Id, c.Flag), если вы хотите сделать код более явным в типах кортежей
Ответ 4
В linq для сущностей вы можете проецировать анонимный тип или на DTO. Чтобы избежать этой проблемы, вы можете использовать метод расширения AsEnumerable
:
codes = codesRepo.SearchFor(predicate).AsEnumerable().
.Select(c => new Tuple<string, byte>(c.Id, c.Flag))
.ToList();
Этот метод позволяет вам работать с Linq to Object вместо Linq для Entities, поэтому после его вызова вы можете проецировать результат запроса в то, что вам нужно. Преимущество использования AsEnumerable
вместо ToList
заключается в том, что AsEnumerable
не выполняет запрос, он сохраняет отложенные выполнение. Хорошая идея всегда фильтровать ваши данные прежде, чем вызвать один из этих методов.
Ответ 5
Я нашел ответ:
codes = codesRepo.SearchFor(predicate)
.ToList()
.Select(c => Tuple.Create(c.Id, c.Flag))
.ToList();
Ответ 6
Используйте этот метод, чтобы сделать это и использовать асинхронный.
var codes = await codesRepo.SearchFor(predicate)
.Select(s => new
{
Id = s.Id,
Flag = s.Flag
}).FirstOrDefaultAsync();
var return_Value = new Tuple<string, byte>(codes.Id, codes.Flag);
Ответ 7
Просто мои два цента: это меня несколько раз поймало с именами типов:
Несколько хороших примеров:
private Tuple<string, byte> v1()
{
return new Tuple<string, byte>("", 1);
}
private (string, int) v2()
{
return ("", 1);
}
private (string Id, byte Flag) v3()
{
return ("", 1);
}
С уважением.