2011-02-10 44 views
14

Tôi có người dùng tìm kiếm các bản ghi loại Record. Họ nhập cụm từ tìm kiếm vào hộp văn bản và sau đó tôi tìm kiếm các bản ghi bằng cách khớp một vài trường với cụm từ tìm kiếm.Cách sử dụng lại các mệnh đề trong các truy vấn LINQ To Sql

truy vấn của tôi trông giống như:

var results = from record in DataContext.Records 
       where 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
       select record; 

Tôi có một số thắc mắc rằng tất cả sử dụng bộ lọc tương tự và do đó tôi muốn trích xuất các bộ lọc để nó có thể được tái sử dụng. Một cái gì đó như:

var filter = new Func<Record, string, bool>(
       (record, term) => 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
      ); 

var results = from record in DataContext.Records 
       where filter(record, term) 
       select record; 

Tuy nhiên, nó không hoạt động vì:

Phương pháp 'System.Object DynamicInvoke (System.Object []) không có bản dịch được hỗ trợ để SQL.

Làm cách nào để tôi có thể sử dụng lại trạng thái của mình trong các truy vấn?

Trả lời

12

Sử dụng CompiledQuery!

var filter = CompiledQuery.Compile(
    (DatabaseDataContext dc, Record record, string term) => 
     record.Field1.ToLower().Contains(term) || 
     record.Field2.ToLower().Contains(term) || 
     record.Field3.ToLower().Contains(term) 
); 

var results = from record in DataContext.Records 
       where filter(DataContext, record, term) 
       select record; 

Để biết thêm thông tin, hãy xem How to: Store and Reuse Queries.

+0

Tôi không nghĩ rằng điều này sẽ làm việc như bằng văn bản - CQ.Compile tạo ra một Func để nó không thể được sử dụng trong mệnh đề Where của một truy vấn LINQ to SQL. Bạn không cần một cái gì đó như 'var query = CompiledQuery.Compile ((string term) => from r trong DataContext.Records trong đó r.Field1.ToLower(). Chứa (term) select r);' then 'var results = truy vấn ("someTerm"); '? Đã không sử dụng CompiledQuery vì vậy tôi có thể sai! – itowlson

+0

@ itowlson: Nó hoạt động. Đây là cách bạn tạo các hàm có thể sử dụng lại có thể sử dụng trong LINQ to SQL. Nhà cung cấp truy vấn sẽ nhận ra hàm được biên dịch và sẽ tạo truy vấn nếu cần. Nó không chỉ tạo ra các truy vấn đầy đủ, nhưng nó cũng có thể tạo ra các phần truy vấn (chẳng hạn như điều kiện này). –

+1

Điều này hoạt động như mong đợi. Điều kỳ lạ duy nhất là tôi cũng tái sử dụng bộ lọc đó trong các truy vấn LINQ To Object, và sau đó tôi vẫn phải chuyển LINQ To Sql DataContext làm tham số. Bất kỳ cách nào để xác định truy vấn độc lập với DataContext? –

1

Tôi nghĩ bạn cần phải đặt số này là Expression<Func<Record, bool>>. Nếu không, nó đang cố gắng dịch C# phương thức gọi thực tế sang SQL chứ không phải mô tả của nó. Đây không phải là đảm bảo rằng phiên bản này sẽ hoạt động; Tôi không chắc các hàm chuỗi nào có thể dịch sang SQL.

12

Bạn cần phải xây dựng một biểu thức thay vì của một hàm:

Expression<Func<Record, bool>> filter = 
    record => record.Field1.ToLower().Contains(term); // rest omitted 

Biểu thức lambda vẫn giữ nguyên, nhưng bạn cần phải trả lại nó vào một biến kiểu Expression<Func<Record, bool>> - đó sẽ làm cho các biên dịch C# biên dịch nó như là một biểu thức thay vì một đại biểu, cho phép nó được chuyển đến LINQ to SQL.

Tuy nhiên, bạn sẽ không thể sử dụng một biến biểu với C# -syntax mệnh đề where: bạn sẽ cần phải sử dụng các phương pháp khuyến nông ở đâu:

var results = DataContext.Records.Where(filter); 

Edited thêm: Nếu bạn muốn để có thể tạo các bộ lọc trên điều khoản khác nhau, bạn chỉ cần một phương pháp để tạo ra một biểu hiện từ một thuật ngữ:

private static Expression<Func<Record, bool>> Filter(string term) 
{ 
    return r => r.Field1.ToLower().Contains(term); 
} 

var results = DataContext.Records.Where(Filter(term)); 

Nếu bạn muốn giữ filter như một lambda là bạn có vào lúc này, bạn có thể làm như vậy, nhưng generics được một chút lồng nhau:

Func<string, Expression<Func<Record, bool>>> filter = 
    term => (r => r.Field1.ToLower().Contains(term)); 

var results = DataContext.Records.Where(filter(term)); 

Bất kể, điều quan trọng là những gì diễn ra trong mệnh đề where phải là một Expression<Func<Record, bool>> - nhưng như trình bày ở trên, bạn có thể làm cho sự biểu hiện phụ thuộc vào term bằng cách xây dựng một biểu thức phù hợp trên bay. Đó là chính xác những gì LINQ to SQL sẽ làm nếu bạn viết ra bộ lọc dài tay trong mệnh đề Where.

+0

Cú pháp C# cho mệnh đề where chỉ là đường biên dịch; nó tạo ra cùng một mã. –

+0

Có, nhưng nếu bộ lọc thuộc loại Expression thay vì Func, thì nó không hỗ trợ cú pháp 'filter (record)'. Bạn nhận được lỗi "bộ lọc là một biến nhưng được sử dụng như một phương thức". Và nếu bạn chỉ cần viết 'nơi lọc' sau đó bạn nhận được 'không thể ngầm chuyển đổi loại Expression (...) thành bool'. – itowlson

+0

Cảm ơn - nó có ý nghĩa. Tuy nhiên tôi đã không quản lý để kéo nó ra bởi vì trong ví dụ (sai) tôi đã đưa ra, 'term' là một đóng trong biểu thức lambda của tôi và sử dụng lại bộ lọc, tôi sẽ cần phải làm cho nó một tham số. Bây giờ bộ lọc của tôi là một 'Biểu thức >' nơi dự kiến ​​một 'Biểu thức >'. Điều này vẫn còn khả thi? –

2

Ngoài vấn đề Expression<Func<Record, bool>> mà người khác đã chỉ ra, tôi khuyên bạn nên xem xét PredicateBuilder. Nó rất tốt để kết hợp động các biểu thức lambda.

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