Buat Tupel dalam Pilihan Linq

89

Saya bekerja dengan C # dan .NET Framework 4.5.1 mengambil data dari database SQL Server dengan Entity Framework 6.1.3.

Aku punya ini:

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

Dan ketika saya menjalankannya, saya mendapatkan pesan ini:

Hanya konstruktor dan penginisialisasi tanpa parameter yang didukung di LINQ ke Entitas.

Saya tidak tahu bagaimana saya harus membuat Tuple karena semua contoh yang saya temukan kebanyakan seperti ini.

Saya sudah mencoba ini:

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

Dan dapatkan kesalahan ini:

LINQ ke Entitas tidak mengenali metode 'System.Tuple`2 [System.String, System.Byte] Buat metode [String, Byte] (System.String, Byte)', dan metode ini tidak dapat diterjemahkan ke dalam ekspresi penyimpanan.

Dimana masalahnya?

VansFannel
sumber
Sepertinya Anda perlu membuat objek yang diketik dengan kuat. Tapi ya, pertanyaan bagus; suara positif.
frenchie

Jawaban:

121

Sementara jawaban oleh octavioccl karya, lebih baik untuk proyek pertama hasil query ke dalam jenis anonim, dan kemudian beralih ke Enumerable dan mengubahnya menjadi tupel. Dengan cara ini kueri Anda akan mengambil dari basis data hanya bidang yang diperlukan.

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

Catatan: Aturan di atas berlaku untuk EF6. EF Core secara alami mendukung tupel (dalam proyeksi atau sebagai kunci gabungan / grup) melalui konstruktor tupel, misalnya kueri asli hanya berfungsi

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

tetapi bukan Tuple.Createmetodenya (EF Core 2.x).

Ivan Stoev
sumber
Solusi yang sangat bagus - Terima kasih! ... Bagaimana jika saya harus memperpanjang solusi ini dengan nilai nullable lainnya? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })tidak bekerja.
skyfrog
2
@skyfrog Operator ?.tidak didukung di pohon ekspresi. Tetapi selain itu, Anda dapat memperluas tipe anonim dengan nilai sebanyak yang Anda inginkan - jangan lupa untuk menamainya jika diperlukan :) misalnyac => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev
1
Bagus! Terima kasih banyak @Ivan atas balasan Anda. Saya sangat dekat! ... tetapi selalu mudah untuk mengatakannya sambil melihat ke belakang ;-)
skyfrog
Jawaban yang bagus, dapat digunakan dengan EF-Entities .. misalnya dbCtx.MyEntity.Where (). Select (.. to anon object ...). Dll ...
joedotnot
50

Hanya jawaban yang diperbarui untuk C # 7, sekarang Anda dapat menggunakan sintaks yang lebih sederhana untuk membuat ValueTuple.

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();

Anda bahkan dapat memberi nama properti tupel sekarang:

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

Jadi, alih-alih menggunakannya sebagai Item1 atau Item2, Anda dapat mengaksesnya sebagai Id atau Bendera.

Lebih banyak dokumen tentang memilih-antara-anonim-dan-tuple

Rafael Merlin
sumber
11

Coba ini:

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

Telah diinformasikan bahwa ini tidak menerima LINQ untuk entitas.

Pilihan lainnya adalah menarik hasil ke dalam memori sebelum memilih. Jika Anda akan melakukan ini, saya akan merekomendasikan melakukan semua pemfilteran sebelum .AsEnumerable () karena itu berarti Anda hanya menarik kembali hasil yang Anda inginkan daripada menarik kembali seluruh tabel dan kemudian memfilter.

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

serta Tuple.Create (c.Id, c.Flag) dapat diubah menjadi Tuple baru (c.Id, c.Flag) jika Anda ingin membuat kode sedikit lebih eksplisit dalam jenis tupel

Dhunt
sumber
Maaf, tidak berhasil. Saya telah memperbarui pertanyaan saya dengan lebih detail.
VansFannel
10

Di linq ke entitas Anda dapat memproyeksikan ke jenis anonim atau ke DTO. Untuk menghindari masalah itu Anda dapat menggunakan AsEnumerablemetode ekstensi:

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

Metode ini memungkinkan Anda bekerja dengan Lini ke Objek alih-alih Lini ke Entitas , jadi setelah memanggilnya, Anda dapat memproyeksikan hasil kueri Anda dalam apa pun yang Anda butuhkan. Keuntungan menggunakan AsEnumerablesebagai gantinya ToListadalah AsEnumerabletidak mengeksekusi kueri, itu mempertahankan eksekusi yang ditangguhkan. Sebaiknya selalu filter data Anda terlebih dahulu sebelum memanggil salah satu metode ini.

octavioccl
sumber
-1 Ini tidak melakukan hal yang sama seperti yang diminta oleh OP. Terkadang, mampu membuat Tuple dalam kueri, untuk tujuan Bergabung itu penting.
Aron
5

Saya telah menemukan jawabannya:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();
VansFannel
sumber
Tidak, ini akan menghasilkan SELECT *
Mihai Bratulescu
1

Gunakan metode ini untuk melakukan ini dan menggunakan 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);
MohammadSoori
sumber
0

Hanya dua sen saya: ini telah menarik perhatian saya beberapa kali dengan nama tipe:

Beberapa contoh yang tidak tepat:

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

Salam.

IbrarMumtaz
sumber
Sintaks yang Anda posting tidak berfungsi. Apa yang mungkin Anda maksudkan untuk menulis adalah public (string Id, byte Flag) SearchFor(Expression predicate), tapi ini bukan intinya. Dua sen seharusnya bukan jawaban, tapi komentar.
M. Stramm
2
Saya memperbarui jawaban saya - saya harus memeriksanya sebelum memposting. Saya tidak setuju; semua info berguna untuk semua pengunjung yang membuka halaman ini terlepas dari bagaimana posisinya. Komentar tidak menyampaikan maksud serta jawaban berkat jawaban.
IbrarMumtaz
Saya setuju bahwa konten yang ditambahkan itu bagus dan komentar tidak sesuai dengan contoh kode. Terima kasih telah mengedit, sekarang jelas bahwa ini bukan jawaban untuk pertanyaan OP (tetapi dapat membantu dengan masalah terkait tupel).
M. Stramm