Saya mencoba menggunakan predikat pada tabel yang digabungkan tetapi saya tidak tahu bagaimana merumuskannya. Sebuah contoh akan menjelaskan masalah terbaik.

Saya punya dua tabel:

  • User tabel (ID int, Name varchar(64), Email varchar(128), Active bit)
  • Event tabel (ID int, Date datetime(2), UserID int (kunci asing), Description varchar(max))

Seorang pengguna dapat memiliki nol hingga banyak peristiwa dan suatu peristiwa dikaitkan dengan tepat satu pengguna (melalui kunci asing).

Sekarang saya memiliki banyak predikat User:

Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";
Expression<Func<User, bool>> isActive = u => u.Active;

Yang dapat saya terapkan saat menanyakan tabel User:

var query1 = ctx.Users.Where(nameIsBob ).Select(u => u);
var query2 = ctx.Users.Where(isActive).Select(u => u);

Namun, jika saya mulai dengan daftar Events dan hanya ingin memfilter peristiwa milik pengguna tertentu, saya ingin melakukan sesuatu seperti ini tetapi saya tidak dapat mengerjakan sintaksnya:

var query3 = ctx.Events
    .Where(e => e.User(nameIsBob))
    .Select(e => new { e.EventID, e.User.UserID });

Masalahnya adalah baris ke-2 di mana predikat tidak dapat dililitkan e.User. Saya juga sudah mencoba:

var query4 = ctx.Events
    .Where(e => e.User.Where(nameIsBob))
    .Select(e => new { e.EventID, e.User.UserID });

Adakah saran tentang cara mendapatkan semua acara milik orang bernama Bob? (dan mungkin ada beberapa pengguna yang disebut Bob). Saya tidak dapat benar-benar memulai dengan tabel Pengguna karena contoh di atas adalah versi sederhana dari apa yang saya lakukan (dan saya harap saya tidak menjelaskan masalah XY!)

1
Chris Walsh 12 Maret 2020, 20:22

1 menjawab

Jawaban Terbaik

Jika ini bukan basis data yang didukung tetapi entitas sisi klien sederhana, Anda dapat mengkompilasi Ekspresi Anda, Anda dapat menggunakannya seperti metode yang mengambil Pengguna dan mengembalikan bool:

Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";

Func<User, bool> compiledNamedIsBob = nameIsBob.Compile();

var query3 = something.Events
    .Where(e => compiledNameIsBob(e.User))

Ini akan menghilangkan Expression<Func<User, bool>> Anda, mengeluarkan Func<User, bool> darinya dan membuat Expression<Func<Event, bool>> di mana Func<Event, bool> menarik peristiwa Pengguna keluar dan memanggil Func<User, bool> menggunakan dia..

PENTING: saran untuk mengompilasi Ekspresi ke Func ini tidak bekerja pada sesuatu seperti kerangka entitas. EF akan mengambil .NET Expression yang tertulis dan menerjemahkannya ke dalam SQL, dan meskipun cukup pintar dalam melakukannya, EF tidak memerlukan metode .NET mandiri (diperoleh dari kompilasi Ekspresi ke Func, di atas) dan pilih terpisah, menerjemahkan pernyataan konstituennya ke SQL. Satu-satunya cara itu bisa berhasil adalah dengan mengunduh seluruh tabel DB ke klien, lalu memanggil metode .NET pada setiap catatan yang diunduh (kecuali jika pekerjaan telah dilakukan agar EF mengirim metode .NET ke SQLServer, kompilasi di sana dan panggil itu sebagai bagian dari kueri. Fungsi .NET dapat ada di SQL Server, tetapi EF tidak melakukan ini)


Begitu...

Anda perlu menghargai bahwa ctx.Events.Where() membutuhkan Expression<Func<Event, bool>> dan Anda memiliki Expression<Func<User, bool>> -> Anda perlu membuat Ekspresi yang berfungsi dengan Acara:

Expression<Func<Event, bool>> eventUserNameIsBob = e => e.User.Name == "Bob";

Yang juga dapat Anda lakukan untuk Acara seperti yang Anda lakukan untuk Pengguna:

//like you did this
Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";
Expression<Func<User, bool>> isActive = u => u.Active;

//you can do this
Expression<Func<Event, bool>> eventUserNameIsBob = e => e.User.Name == "Bob");

Dan terapkan seperti yang Anda terapkan ke tabel pengguna:

var query1 = ctx.Users.Where(nameIsBob); //returns Users
var query2 = ctx.Users.Where(isActive); //returns Users
var query3 = ctx.Events.Where(eventUserNameIsBob).Select(e => e.User); //returns Users, via the Select

Namun, jika Anda hanya ingin memiliki ekspresi nameIsBob, bukan ekspresi eventUserNameIsBob, Anda perlu memahami bahwa ekspresi nameIsBob hanya berfungsi pada koleksi yang dapat dikueri Pengguna jadi Anda harus mengambil semua acara Anda dan menggali pengguna dari mereka, dan menjalankan ekspresi pada Pengguna yang digali, yang berarti Anda pada dasarnya kehilangan pengetahuan tentang Acara Anda (Anda harus mendapatkannya kembali dengan bertanya kepada Pengguna):

var query4 = ctx.Events.Select(e => e.User).Where(nameIsBob);

Ini tidak mengembalikan Anda "sekelompok pengguna" seperti kueri lainnya (ctx.Users.Where(nameIsBob)), tetapi Anda telah kehilangan acara kecuali Anda mendapatkannya kembali melalui pengguna:

var query4 = ctx.Events.Select(e => e.User).Where(nameIsBob).Select(u => u.Event);

Apa yang akan dilakukan DB Anda dalam kasus ini, saya tidak yakin; Anda harus mengujinya/mencatat SQL yang dihasilkan.

Secara pribadi saya akan membuat ekspresi baru yang memahami peristiwa seperti di atas (eventUserNameIsBob) atau saya tidak akan repot-repot menyampaikan ekspresi dan saya hanya akan mendefinisikannya sebaris:

var query3 = ctx.Events.Where(e => e.User.Name == "Bob"); //events owned by bobs

ORM DB modern dapat membongkar ini menjadi SELECT events.* FROM events JOIN users ON (...) WHERE users.name = 'bob' daripada di masa lalu di mana akan seperti:

SELECT id FROM users WHERE name = 'bob' --returns list of 1,2,4,5
SELECT * FROM events WHERE userid = 1
SELECT * FROM events WHERE userid = 2
SELECT * FROM events WHERE userid = 4
SELECT * FROM events WHERE userid = 5
1
Community 20 Juni 2020, 09:12