Створіть кортеж у 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 підтримуються лише безпараметричні конструктори та ініціалізатори.

Я не знаю, як мені доводиться створювати Tuple, тому що всі приклади, які я знайшов, здебільшого схожі на цей.

Я спробував це:

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)', і цей метод не може бути перекладений у вираз магазину.

Де проблема?


Схоже, вам потрібно буде створити сильно набраний об’єкт. Але так, гарне запитання; проголосував.
французька

Відповіді:


119

Поки відповідь за допомогою octavioccl працює, краще спочатку спроектувати результат запиту в анонімний тип, а потім переключитися на enumerable і перетворити його на кортеж. Таким чином ваш запит отримає з бази даних лише необхідні поля.

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


Дуже хороше рішення - спасибі! ... А як щодо того, якби мені довелося розширити це рішення на інше значення, що обнуляється? .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 (). Виберіть (.. для відключення об'єкта ...). Тощо ...
joedotnot

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 для організацій.

Іншим варіантом було б зберегти результат у пам’ять перед вибором. Якщо ви збираєтеся це зробити, я б рекомендував зробити всю фільтрацію до .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

11

У linq до сутностей ви можете проектувати на анонімний тип або на 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

Використовуйте цей метод для цього та використовуйте асинхронну.

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.