Kerangka kerja entitas, permintaan kueri Sertakan () beberapa entitas anak

176

Ini mungkin pertanyaan yang sangat sederhana tapi apa cara yang bagus untuk memasukkan banyak entitas anak saat menulis kueri yang mencakup TIGA level (atau lebih)?

yaitu saya memiliki 4 tabel: Company, Employee, Employee_CardanEmployee_Country

Perusahaan memiliki hubungan 1: m dengan Karyawan.

Karyawan memiliki hubungan 1: m dengan Employee_Car dan Employee_Country.

Jika saya ingin menulis kueri yang mengembalikan data dari semua 4 tabel, saat ini saya menulis:

Company company = context.Companies
                         .Include("Employee.Employee_Car")
                         .Include("Employee.Employee_Country")
                         .FirstOrDefault(c => c.Id == companyID);

Pasti ada cara yang lebih elegan! Ini bertele-tele dan menghasilkan SQL menghebohkan

Saya menggunakan EF4 dengan VS 2010

Nathan Liu
sumber

Jawaban:

201

Gunakan metode ekstensi . Ganti NameOfContext dengan nama konteks objek Anda.

public static class Extensions{
   public static IQueryable<Company> CompleteCompanies(this NameOfContext context){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country") ;
     }

     public static Company CompanyById(this NameOfContext context, int companyID){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country")
             .FirstOrDefault(c => c.Id == companyID) ;
      }

}

Maka kode Anda menjadi

     Company company = 
          context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID);

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);
Nix
sumber
Tapi saya ingin menggunakannya seperti ini: //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
Hamid
Bullsye Nix. Ekstensi harus menjadi porta panggilan pertama untuk ... yah ... memperluas fungsionalitas yang telah ditentukan.
Datang
12
Bertahun-tahun kemudian, saya tidak akan merekomendasikan menyertakan berbasis string, karena mereka tidak aman runtime. Jika nama properti navigasi pernah berubah atau salah eja, itu akan rusak. Sangat menyarankan untuk menggunakan include yang diketikkan.
Jeff Putz
2
sejak pengenalan nama (kelas) dimungkinkan untuk menggunakan pendekatan ini dengan aman. Jika nama entitas berubah, itu akan diambil saat kompilasi. Contoh: context.Companies.Include (nameof (Karyawan)) Jika seseorang perlu melangkah lebih jauh, nama harus bersesuaian dengan nameof (Karyawan) + "." + Nameof (Employee_Car)
Karl
Teknik metode ekstensi tidak berfungsi untuk kueri yang dikompilasi (setidaknya tidak pada EFCore) yang dikonfirmasi di sini: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge
156

EF 4.1 hingga EF 6

Ada yang sangat diketik.Include yang memungkinkan kedalaman pemuatan yang diinginkan untuk ditentukan dengan memberikan ekspresi Pilih ke kedalaman yang sesuai:

using System.Data.Entity; // NB!

var company = context.Companies
                     .Include(co => co.Employees.Select(emp => emp.Employee_Car))
                     .Include(co => co.Employees.Select(emp => emp.Employee_Country))
                     .FirstOrDefault(co => co.companyID == companyID);

Sql yang dihasilkan di kedua instance masih belum intuitif, tetapi tampaknya cukup performant. Saya telah memberikan contoh kecil di GitHub di sini

EF Core

EF Core memiliki metode ekstensi baru .ThenInclude(), meskipun sintaksnya sedikit berbeda :

var company = context.Companies
                     .Include(co => co.Employees)
                           .ThenInclude(emp => emp.Employee_Car)
                      ...

Sesuai dokumen, saya akan menyimpan 'indentasi' tambahan .ThenIncludeuntuk menjaga kewarasan Anda.

Info Usang (Jangan lakukan ini):

Pemuatan banyak cucu dapat dilakukan dalam satu langkah, tetapi ini membutuhkan pembalikan yang agak canggung grafik sebelum menuju ke simpul berikutnya (NB: Ini TIDAK bekerja dengan AsNoTracking()- Anda akan mendapatkan kesalahan runtime):

var company = context.Companies
         .Include(co => 
             co.Employees
                .Select(emp => emp.Employee_Car
                    .Select(ec => ec.Employee)
                    .Select(emp2 => emp2.Employee_Country)))
         .FirstOrDefault(co => co.companyID == companyID);

Jadi saya akan tetap dengan opsi pertama (satu Sertakan per model kedalaman entitas daun).

StuartLC
sumber
4
Saya bertanya-tanya bagaimana melakukannya dengan sangat diketik. Sertakan pernyataan. Memproyeksikan anak-anak dengan Select adalah jawabannya!
1
Standar saya tentang "co.Employees.Select (...)" menunjukkan kesalahan sintaks pada "Pilih", mengatakan bahwa "'Karyawan' tidak mengandung definisi untuk 'Pilih' [atau metode ekstensi]". Saya sudah memasukkan System.Data.Entity. Saya hanya ingin mendapatkan satu kolom dari tabel bergabung.
Chris Walsh
1
Saya memiliki tabel induk yang mereferensikan tabel anak yang sama dua kali. Dengan string yang lama termasuk sintaksis sulit untuk melakukan preload hubungan yang benar. Cara ini jauh lebih spesifik. Harap diingat untuk memasukkan namespace System.Data.Entity untuk include yang sangat diketik.
Karl
1
Dengan .net core 2.1, saya membutuhkan namespace Microsoft.EntityFrameworkCore, bukannya System.Data.Entity
denvercoder9
27

Anda mungkin menemukan artikel yang menarik ini yang tersedia di codeplex.com .

Artikel ini menyajikan cara baru mengekspresikan kueri yang menjangkau beberapa tabel dalam bentuk bentuk grafik deklaratif.

Selain itu, artikel tersebut berisi perbandingan kinerja menyeluruh dari pendekatan baru ini dengan pertanyaan EF. Analisis ini menunjukkan bahwa GBQ dengan cepat mengungguli kueri EF.

Merijn
sumber
bagaimana ini bisa diterapkan dalam aplikasi dunia nyata?
Victor. Uduak