2013-08-20 62 views
11

Tôi đang cố gắng để tạo ra một phương pháp lọc cho Entity Danh sách khuôn khổ và hiểu rõ hơn về Expression<Func<...Entity Framework Lọc "Expression <Func <T, bool>>"

Tôi có một thử nghiệm chức năng như thế này.

public IQueryable<T> Filter<T>(IEnumerable<T> src, Expression<Func<T, bool>> pred) 
{ 
    return src.AsQueryable().Where(pred); 
} 

và nếu tôi làm điều này:

context.Table.Filter(e => e.ID < 500); 

hay này:

context.Table.Filter(e => e.SubTable.Where(et => et.ID < 500).Count() > 0 && e.ID < 500); 

nó tất cả hoạt động tốt.

Nhưng nếu tôi làm điều này:

context.Table.Filter(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500); 

hay này:

context.Table.Where(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500); 

tôi nhận được một lỗi. LINQ to Entities does not recognize the method ...Filter...

Tại sao nó hoạt động trong một trường hợp chứ không phải trong trình bổ sung? Tôi nên thay đổi gì trong Bộ lọc để nó hoạt động với các bảng có liên quan. Tôi thích tránh xa các thư viện bên ngoài khác như những gì tôi muốn là tìm hiểu cách hoạt động và có thể sử dụng nó trong bất kỳ kịch bản nào trong tương lai.

Trong hai trường hợp đầu tiên bộ lọc chạy trong cơ sở dữ liệu chính xác.

Trả lời

20

Jon và Tim đã giải thích lý do tại sao nó không hoạt động.

Giả sử rằng mã bộ lọc bên trong Filter không phải là tầm thường, bạn có thể thay đổi Filter để nó trả về một biểu thức mà EF có thể dịch.

Giả sử bạn có mã này:

context.Table.Where(x => x.Name.Length > 500); 

Bây giờ bạn có thể tạo ra một phương pháp trở về biểu thức này:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length) 
{ 
    return x => x.Name.Length > length; 
} 

Cách sử dụng sẽ là như thế này:

context.Table.Where(FilterByNameLength(500)); 

Các biểu thức bạn xây dựng bên trong FilterByNameLength có thể tùy ý phức tạp miễn là bạn có thể vượt qua nó trực tiếp đến Where.

+0

bởi rất nhiều đọc và dùng thử và lỗi tôi nghi ngờ đây là cách duy nhất. nó sẽ làm việc các trường hợp như context.Table.Where (e => e.subTable.Any (MyFilter())); –

+0

@ Pedro.The.Kid: Không, nó sẽ không. Kết quả của phương thức này cần phải được chuyển tới ngoài cùng bên ngoài 'Where', bởi vì mọi biểu thức mà bạn truyền tới ngoài cùng bên ngoài' Where' không thực sự được thực hiện nhưng được diễn giải. –

+0

Tôi đã thực hiện một thử nghiệm và điều này hoạt động var lọc = MyFilter(); context.Table.Where (e => e.subTable.AsQueryable() .bất kỳ (bộ lọc)); –

4

Tại sao nó hoạt động trong một trường hợp chứ không phải trong trình bổ sung?

Vì EF không thực sự "biết" về phương pháp Filter của bạn. Nó không có sự hiểu biết về những gì nó có nghĩa là để làm, vì vậy nó không biết làm thế nào để dịch nó vào SQL. So sánh điều đó với Where v.v., mà nó không hiểu.

Phiên bản nơi bạn gọi nó là trực tiếp trên bảng ban đầu làm việc vì như vậy bạn không kết thúc với một cây biểu thức có chứa một cuộc gọi đến Filter - nó chỉ gọi Filter trực tiếp, mà lần lượt không xây dựng lên một truy vấn ... nhưng một câu hỏi mà EF hiểu.

Tôi sẽ rất ngạc nhiên nếu bạn có thể tìm ra cách để có được phương pháp Filter để làm việc trong một truy vấn EF ... nhưng bạn đã từng nói rằng sử dụng Where vẫn hoạt động, vậy tại sao lại sử dụng Filter? Tôi muốn sử dụng phiên bản Where - hoặc tốt hơn, sử dụng quá tải Any mà phải mất một vị ngữ:

context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500); 
+0

có cách nào nó có thể được thay đổi để kết thúc với một biểu thức phù thủy cây liều không bao gồm tên phương pháp? –

+0

@ Pedro.The.Kid: Không dễ dàng. Bạn có thể viết một phương thức viết lại cây biểu thức và tạo một truy vấn mới dựa trên đó, nhưng không rõ tại sao bạn muốn làm như vậy và nó sẽ phức tạp. –

+0

như cho việc sử dụng Nơi nó chỉ để dễ dàng kiểm tra trong môi trường thực tế nó sẽ phức tạp hơn nhiều. –

6

Đó là hữu ích để hiểu sự khác biệt giữa Expression<Func<>>Func<>.

Một Expressione => e.ID < 500 lưu trữ các thông tin về biểu rằng: rằng có một Te, rằng bạn đang truy cập vào bất động sản ID, kêu gọi các nhà điều hành < với int giá trị 500.Khi EF nhìn vào nó, nó có thể biến nó thành một cái gì đó như [SomeTable].[ID] < 500.

Một Funce => e.ID < 500 là một phương pháp tương đương với:

static bool MyMethod(T e) { return e.ID < 500; } 

Nó được biên soạn dưới dạng mã IL mà thực hiện điều này; nó không được thiết kế để được 'tái tạo' thành một truy vấn SQL hay bất cứ thứ gì khác, chỉ chạy.

Khi EF lấy Expression của bạn, nó phải hiểu mọi phần của nó, bởi vì nó sử dụng nó để xây dựng một truy vấn SQL. Nó được lập trình để biết phương thức Where hiện có có nghĩa là gì. Nó không biết phương thức Filter của bạn có nghĩa là, mặc dù nó là một phương pháp tầm thường, vì vậy nó chỉ từ bỏ.