Создание кортежа в Linq Select


87

Я работаю с C # и .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 to Entities поддерживаются только конструкторы и инициализаторы без параметров.

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

Я пробовал это:

codes = codesRepo.SearchFor(predicate)
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

И получите эту ошибку:

LINQ to Entities не распознает метод 'System.Tuple`2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte) », и этот метод нельзя преобразовать в выражение хранилища.

В чем проблема?


Похоже, вам нужно создать строго типизированный объект. Но да, хороший вопрос; проголосовали за.
frenchie

Ответы:


119

Хотя ответ по 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).


Очень хорошее решение - Спасибо! ... Что если бы мне пришлось расширить это решение другим значением, допускающим значение NULL? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })не работает.
skyfrog

2
@skyfrog Оператор ?.не поддерживается в деревьях выражений. Но кроме этого, вы можете расширить анонимный тип, c => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Иван Стоев

1
Большой! Большое спасибо @Ivan за ваш ответ. Я был так близок! ... но всегда легко сказать, оглядываясь назад ;-)
skyfrog

Отличный ответ, можно использовать с EF-Entities .. например dbCtx.MyEntity.Where (). Select (.. to anon object ...).
Etc

45

Просто обновленный ответ для C # 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 }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

Поэтому вместо того, чтобы использовать его как Item1 или Item2, вы можете получить к нему доступ как Id или Flag.

Дополнительные документы по выбору между анонимным и кортежем


11

Попробуй это:

codes = codesRepo.SearchFor(predicate)
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

Было сообщено, что это не принимается в LINQ to entity.

Другой вариант - вытащить результат в память перед выбором. Если вы собираетесь это сделать, я бы рекомендовал выполнить всю фильтрацию до .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), если вы хотите сделать код более явным в типах кортежей


Извините, это не работает. Я добавил в свой вопрос более подробную информацию.
VansFannel 05

11

В linq to entity вы можете проецировать на анонимный тип или на DTO. Чтобы избежать этой проблемы, вы можете использовать AsEnumerableметод расширения:

codes = codesRepo.SearchFor(predicate).AsEnumerable().
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

Этот метод позволяет вам работать с Linq to Object вместо Linq to Entities , поэтому после его вызова вы можете проецировать результат своего запроса на все, что вам нужно. Преимущество использования AsEnumerableвместо этого ToListзаключается в том, что AsEnumerableон не выполняет запрос, а сохраняет отложенное выполнение. Перед вызовом одного из этих методов рекомендуется всегда фильтровать данные.



1

Используйте этот метод для этого и используйте файл async.

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);

0

Всего лишь два цента: несколько раз меня привлекали названия типов:

Несколько интересных примеров:

    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);
    }

С уважением.


Опубликованный вами синтаксис не работает. Вы, наверное, хотели написать public (string Id, byte Flag) SearchFor(Expression predicate), но это не к делу. Два цента должны быть не ответом, а комментарием.
M.Stramm

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

Я согласен с тем, что добавленный контент хорош, а комментарии не подходят для примеров кода. Спасибо за редактирование, теперь ясно, что это не ответ на вопрос OP (но может помочь с проблемами, связанными с кортежами).
M.Stramm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.