Truy vấn linq khung thực thể Bao gồm () nhiều thực thể con


176

Đây có thể là một câu hỏi thực sự yếu tố nhưng một cách hay để bao gồm nhiều thực thể con khi viết một truy vấn kéo dài BA cấp độ (hoặc nhiều hơn)?

tức là tôi có 4 bảng: Company, Employee, Employee_CarEmployee_Country

Công ty có mối quan hệ 1: m với Nhân viên.

Nhân viên có mối quan hệ 1: m với cả Employee_Car và Employee_Country.

Nếu tôi muốn viết một truy vấn trả về dữ liệu từ cả 4 bảng, tôi hiện đang viết:

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

Phải có một cách thanh lịch hơn! Điều này kéo dài và tạo ra SQL khủng khiếp

Tôi đang sử dụng EF4 với VS 2010

Câu trả lời:


201

Sử dụng các phương pháp mở rộng . Thay thế NameOfContext bằng tên của bối cảnh đối tượng của bạn.

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

}

Sau đó, mã của bạn trở thành

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

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);

Nhưng tôi muốn sử dụng nó như thế này: //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. Tiện ích mở rộng phải là cổng gọi đầu tiên cho ... à ... mở rộng chức năng được xác định trước.
Đến vào

12
Nhiều năm sau, tôi không khuyến nghị sử dụng chuỗi dựa trên, vì chúng không an toàn cho thời gian chạy. Nếu tên thuộc tính điều hướng từng thay đổi hoặc sai chính tả, nó sẽ bị hỏng. Thay vào đó, mạnh mẽ đề nghị sử dụng đánh máy bao gồm.
Jeff Putz

2
kể từ khi giới thiệu nameof (lớp), có thể sử dụng phương pháp này một cách an toàn. Trong trường hợp tên thực thể thay đổi, nó sẽ được chọn trong quá trình biên dịch. Ví dụ: bối cảnh.Compiances.Include (nameof (Employee)) Trong trường hợp người ta cần đi sâu hơn tên phải liên kết với nameof (Employee) + "." + Nameof (Employee_Car)
Karl

Kỹ thuật phương thức mở rộng không hoạt động đối với các truy vấn đã biên dịch (ít nhất là không phải trên EFCore) được xác nhận tại đây: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge

156

EF 4.1 đến EF 6

Có một kiểu gõ mạnh.Include cho phép chỉ định độ sâu tải háo hức cần thiết bằng cách cung cấp các biểu thức Chọn đến độ sâu thích hợp:

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 được tạo trong cả hai trường hợp vẫn không có nghĩa là trực quan, nhưng dường như đủ hiệu quả. Tôi đã đặt một ví dụ nhỏ trên GitHub tại đây

Lõi EF

EF Core có một phương thức mở rộng mới .ThenInclude(), mặc dù cú pháp hơi khác nhau :

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

Theo các tài liệu, tôi sẽ giữ thêm 'thụt lề' .ThenIncludeđể giữ gìn sự tỉnh táo của bạn.

Thông tin lỗi thời (Đừng làm điều này):

Việc tải nhiều cháu có thể được thực hiện trong một bước, nhưng điều này đòi hỏi một sự đảo ngược khá khó xử khi sao lưu đồ thị trước khi đi xuống nút tiếp theo (NB: Điều này KHÔNG hoạt động với AsNoTracking()- bạn sẽ gặp lỗi thời gian chạy):

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

Vì vậy, tôi sẽ ở lại với tùy chọn đầu tiên (một mô hình độ sâu thực thể Bao gồm mỗi lá).


4
Tôi đã tự hỏi làm thế nào để làm điều đó với các câu lệnh được gõ mạnh. Chiếu trẻ em với Chọn là câu trả lời!

1
Phương trình "co.Emprocod.Select (...)" của tôi hiển thị lỗi cú pháp trên "Chọn", nói rằng "'Nhân viên' không chứa định nghĩa cho 'Chọn' [hoặc phương thức mở rộng]". Tôi đã bao gồm System.Data.Entity. Tôi chỉ muốn lấy một cột duy nhất từ ​​bảng đã tham gia.
Chris Walsh

1
Tôi đã có một bảng cha mẹ đã tham chiếu cùng một bảng con hai lần. Với chuỗi cũ bao gồm cú pháp, thật khó để tải trước mối quan hệ đúng. Cách này cụ thể hơn rất nhiều. Xin lưu ý bao gồm không gian tên System.Data.Entity cho kiểu gõ mạnh bao gồm.
Karl

1
Với lõi .net 2.1 Tôi cần không gian tên Microsoft.EntityFrameworkCore thay vì System.Data.Entity
denvercoder9

27

Bạn có thể tìm thấy bài viết quan tâm này có sẵn tại codeplex.com .

Bài viết trình bày một cách mới để thể hiện các truy vấn trải rộng trên nhiều bảng dưới dạng hình dạng biểu đồ khai báo.

Hơn nữa, bài viết chứa một so sánh hiệu suất kỹ lưỡng của phương pháp mới này với các truy vấn EF. Phân tích này cho thấy GBQ nhanh chóng vượt trội hơn các truy vấn EF.


Làm thế nào điều này có thể được thực hiện trong một ứng dụng trong thế giới thực?
Victor.Uduak

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.