LINQ To Entities tidak mengenali metode Last. Betulkah?

144

Dalam kueri ini:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderBy(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.Last());
}

Saya harus beralih ke ini agar bisa berfungsi

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderByDescending(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.FirstOrDefault());
}

Saya bahkan tidak bisa menggunakan p.First(), untuk mencerminkan permintaan pertama.

Mengapa ada batasan dasar seperti itu dalam apa yang sebaliknya sistem ORM yang kuat?

bevacqua
sumber
simpan objek IEnumrable Anda ke dalam variabel baru, lalu kembalikan variabel.last (). ini akan bekerja.
Ali.Rashidi

Jawaban:

220

Keterbatasan itu bermuara pada kenyataan bahwa pada akhirnya ia harus menerjemahkan permintaan itu ke SQL dan SQL memiliki SELECT TOP(dalam T-SQL) tetapi bukan SELECT BOTTOM(tidak ada hal seperti itu).

Namun ada cara mudah untuk itu, hanya memesan turun dan kemudian melakukan First(), yang adalah apa yang Anda lakukan.

EDIT: Penyedia lain mungkin akan memiliki implementasi yang berbeda SELECT TOP 1, di Oracle mungkin akan menjadi sesuatu yang lebih sepertiWHERE ROWNUM = 1

EDIT:

Alternatif lain yang kurang efisien - Saya TIDAK merekomendasikan ini! - adalah untuk memanggil .ToList()data Anda sebelumnya .Last(), yang akan segera menjalankan Ekspresi LINQ To Entities yang telah dibangun hingga saat itu, dan kemudian .Last () Anda akan berfungsi, karena pada saat .Last()itu dieksekusi secara efektif dalam konteks LINQ ke Objects Expression. (Dan seperti yang Anda tunjukkan, itu bisa mengembalikan ribuan catatan dan membuang banyak objek pematerialan CPU yang tidak akan pernah digunakan)

Sekali lagi, saya tidak akan merekomendasikan melakukan hal ini, tetapi itu membantu menggambarkan perbedaan antara di mana dan kapan ekspresi LINQ dieksekusi.

Neil Fenwick
sumber
dan bagaimana LINQ To SQL menangani skenario ini?
bevacqua
@Neil ya saya tahu saya bisa menelepon ToList, tapi saya lebih suka tidak mengambil ribuan catatan dari database hanya untuk menyaringnya hingga lima catatan
bevacqua
2
Jika Anda tahu permintaan Anda akan memberikan hasil kecil, menelepon ToListtidak terlalu buruk.
Justin Skiles
35

Alih-alih Last(), Coba ini:

model.OrderByDescending(o => o.Id).FirstOrDefault();
Azarsa
sumber
14

Ganti Last()oleh pemilih LinqOrderByDescending(x => x.ID).Take(1).Single()

Sesuatu seperti itu akan berhasil jika Anda lebih suka melakukannya di Linq:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Ema.H
sumber
1
Apakah ada alasan untuk menggunakan .Take (1) .Single () alih-alih .FirstOrDefault ()?
Tot Zam
2
@TotZam Pengganti yang valid adalah .First () dalam kasus itu, karena Single () melempar pengecualian jika jumlah item tidak tepat 1.
MEMark
0

Namun cara lain mendapatkan elemen terakhir tanpa OrderByDescending dan memuat semua entitas:

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();
Stas Boyarincev
sumber
0

Itu karena LINQ ke Entitas (dan basis data secara umum) tidak mendukung semua metode LINQ (lihat di sini untuk detail: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

Yang Anda butuhkan di sini adalah memesan data sedemikian rupa sehingga catatan "terakhir" menjadi "pertama" dan kemudian Anda dapat menggunakan FirstOrDefault. Perhatikan bahwa databasese biasanya tidak memiliki konsep seperti "pertama" dan "terakhir", tidak seperti catatan yang paling baru disisipkan akan "terakhir" dalam tabel.

Metode ini dapat menyelesaikan masalah Anda

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Mohammad Hassani
sumber
-2

Menambahkan fungsi tunggal AsEnumerable()sebelum fungsi Pilih bekerja untuk saya.
Contoh:

return context.ServerOnlineCharacters
    .OrderByDescending(p => p.ServerStatus.ServerDateTime)
    .GroupBy(p => p.RawName).AsEnumerable()
    .Select(p => p.FirstOrDefault());

Ref: https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys

Artem Levitin
sumber
Disarankan untuk memasukkan kode kerja dari tautan ke jawaban Anda. Jawaban hanya tautan akan menarik perhatian negatif. Harap berikan jawaban lengkap dengan menambahkan kode yang Anda temukan untuk membantu dan menyelesaikan masalah. Ini menyelesaikan masalah tautan yang tidak berfungsi karena 404 kesalahan di masa mendatang.
Studocwho
1
menambahkan contoh pada jawaban saya
Artem Levitin
Sisi bawah dari jawaban ini adalah bahwa ia akan membawa semua hasil sebelum sisi server "AsEnumerable" dan kemudian pilih yang pertama. Ini bisa sangat tidak diinginkan. (Saya memiliki situasi seperti ini di mana hasilnya memakan waktu 20+ detik karena catatan 20k + dibawa ke sisi-server, begitu saya memindahkannya kembali ke sisi DB, hasilnya dikembalikan dalam waktu kurang dari satu detik)
TChadwick