2010-12-30 43 views
8

Tôi tình cờ gặp một số hành vi LINQ to SQL lạ - có ai có thể làm sáng tỏ điều này không?LINQ to SQL: Tái sử dụng biểu thức lambda

Tôi muốn xác định một biểu thức lambda và sử dụng nó trong câu lệnh LINQ của tôi. Các mã sau hoạt động tốt:

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 
[...] 

Nhưng khi tôi cố gắng sử dụng biểu thức lambda của tôi trong một tuyên bố trên một bảng liên

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 
[...] 

tôi nhận được một ngoại lệ:

Unsupported overload used for query operator 'Any'. 

Nhưng, và điều này tôi không nhận được: Nó hoạt động tốt khi tôi đặt lambda của tôi trực tiếp vào truy vấn:

[...] 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 
[...] 

TẠI SAO ?!

Cảm ơn.

+0

Hãy thử sử dụng 'var lamda = x => x.Id> 1000;'. Không biết điều đó sẽ hữu ích, nhưng có thể ... – Alxandr

+0

@Alxandr - điều đó không thực sự hợp pháp. Các biểu thức lambda có thể được biên dịch thành 'Func <>' hoặc 'Expression >' và trong ví dụ của bạn trình biên dịch sẽ không thể biết bạn muốn cái nào và sẽ ném một lỗi. –

Trả lời

18

OK, đây là giao dịch: dataContext.Table1s thuộc loại IQueryable<T>. IQueryable<T> xác định các phương thức WhereAny có một biến vị ngữ loại Expression<Func<T, bool>>. Các wrapper Expression<> là rất quan trọng, vì đây là những gì cho phép LINQ to SQL dịch biểu thức lambda của bạn thành SQL và thực hiện nó trên máy chủ cơ sở dữ liệu.

Tuy nhiên, IQueryable<T> cũng bao gồm IEnumerable<T>. IEnumerable<T> cũng định nghĩa các phương thức WhereAny, nhưng phiên bản IEnumerable có một biến vị ngữ loại Func<T, bool>. Bởi vì đây là một hàm được biên dịch và không phải là một biểu thức, nó không thể được dịch sang SQL. Kết quả là, mã này ...

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 

... sẽ kéo MỌI bản ghi ra khỏi Table1s vào bộ nhớ, sau đó lọc các bản ghi trong bộ nhớ. Nó hoạt động, nhưng nó thực sự là tin xấu nếu bảng của bạn lớn.

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 

Phiên bản này có hai biểu thức lambda. Bản thứ hai, được chuyển trực tiếp vào Where, là Expression bao gồm tham chiếu đến một số Func. Bạn không thể kết hợp cả hai và thông báo lỗi bạn đang nhận được cho bạn biết rằng cuộc gọi đến Any đang mong đợi một số Expression nhưng bạn đang chuyển vào một số Func.

var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 

Trong phiên bản này, lambda bên trong của bạn được tự động được chuyển đổi thành một Expression vì đó là lựa chọn duy nhất nếu bạn muốn mã của bạn để được chuyển đổi thành SQL bởi LINQ to SQL. Trong các trường hợp khác, bạn buộc lambda phải là Func thay vì Expression - trong trường hợp này là không, vì vậy nó hoạt động.

Giải pháp là gì? Nó thực sự khá đơn giản:

Expression<Func<Table1, bool>> lambda = x => x.Id > 1000; 
+1

+1 giải thích thực sự tốt. –

+0

Cảm ơn. Thật không may nó sẽ không biên dịch, lỗi biên dịch là: Đối số 2: không thể chuyển đổi từ 'System.Linq.Expressions.Expression >' thành 'System.Func ' –

+1

Thông báo lỗi đó dường như chỉ ra rằng bạn đang gọi 'Where' hoặc' Any' trên một đối tượng thực hiện IEnumerable nhưng không IQueryable - bởi vì các phiên bản [IEnumerable] (http://msdn.microsoft.com/en-us/library/bb534803.aspx) mong đợi một Func, trong đó các phiên bản [IQueryable] (http://msdn.microsoft.com/en-us/library/bb548547.aspx) mong đợi một Biểu thức. Nếu nó không chấp nhận một Biểu thức, đối tượng có thể không phải là IQueryable ... –

0

Table1 tham chiếu đến cùng một không gian tên không? Trong ví dụ đầu tiên bạn đang truy vấn đối tượng Table1 trực tiếp dưới dataContext, trong ví dụ thứ hai bạn đang truy vấn đối tượng Table1 là thuộc tính của đối tượng Table2 và trong ví dụ cuối cùng bạn đang sử dụng ẩn danh chức năng khắc phục sự cố.

tôi sẽ tìm kiếm các loại của Table1 đối tượng đó là một tài sản của một đối tượng Table2 và so sánh nó với một đối tượng Table1 được kết nối trực tiếp đến dataContext. Tôi đoán là chúng khác nhau và biểu thức lambda của bạn đang sử dụng loại đối tượng được kết nối với dataContext.

Các vấn đề liên quan