2012-04-27 27 views
10

Tôi có một bộ lọc mà tôi sử dụng trên nhiều phương pháp:Làm thế nào để sử dụng System.Linq.Expressions.Expression để lọc dựa trên trẻ em?

Expression<Func<Child, bool>> filter = child => child.Status == 1; 

(thực sự là phức tạp hơn thế nữa)

Và tôi phải làm như sau

return db.Parents.Where(parent => parent.Status == 1 && 
            parent.Child.Status == 1); 

nơi điều kiện là giống như trong bộ lọc ở trên.

Tôi muốn sử dụng lại bộ lọc theo phương pháp này. Nhưng tôi không biết làm thế nào. Tôi cố gắng

return db.Parents.Where(parent => parent.Status == 1 && 
            filter(parent.Child)); 

nhưng một Expression không thể được sử dụng như một phương pháp

Trả lời

3

Nếu bạn muốn kết hợp các biểu thức và vẫn có thể sử dụng LINQ-to-sql, bạn có thể muốn xem LinqKit. Nó đi vào bên trong biểu thức của bạn và thay thế tất cả các cuộc gọi hàm theo nội dung của chúng trước khi chuyển đổi sql.

Bằng cách này bạn sẽ có thể sử dụng trực tiếp

return db.Parents 
     .AsExpandable() 
     .Where(parent => parent.Status == 1 && filter(parent.Child)); 
1

Bạn có thể thử này:

var compiledFilter = filter.Compile(); 
foreach (var parent in db.Parents.Where(parent => parent.Status == 1)) 
    if (compiledFilter(parent.Child)) 
     yield return parent; 

Nó đòi hỏi bạn phải kéo tất cả các bậc cha mẹ, nhưng không giống như @ HugoRune của giải pháp, nó không yêu cầu mối quan hệ 1: 1 của Phụ huynh: Trẻ em.

Tôi không nghĩ rằng điều này sẽ có ích cho tình huống của bạn bởi vì các loại khác nhau có liên quan, nhưng chỉ trong trường hợp, đây là một ví dụ về cách bạn có thể kết hợp Expression s: How do I combine LINQ expressions into one?

Edit: Tôi đã từng đề xuất sử dụng Compile(), nhưng điều đó không hoạt động trên LINQ-to-SQL.

+0

Tôi không nghĩ bạn có thể biên dịch biểu thức bên trong câu lệnh linq-to-sql. Lambdas chỉ hoạt động bình thường linq – HugoRune

+0

Hm, có vẻ như bạn nói đúng, thật không may. Tôi sẽ đề nghị một lựa chọn khác. –

+0

Tôi nghĩ tôi sẽ sử dụng các biểu thức kết hợp hoặc Dinamic Linq, vì @HugoRune đề xuất –

1

Vâng, nếu có một 1: mối quan hệ 1 giữa cha mẹ và con (khó, nhưng ví dụ dường như ngụ ý rằng) sau đó bạn có thể làm điều đó như thế này:

return db.Parents 
    .Where(parent => parent.Status == 1) 
    .Select(parent => parent.Child) 
    .Where(filter) 
    .Select(child=> child.Parent); 

Nếu không nó sẽ cứng .

Bạn có thể làm điều đó với dynamic linq nhưng đó có thể là quá mức cần thiết.

Bạn có thể generate your expression tree manually, nhưng điều đó cũng khá phức tạp. Tôi đã không thử bản thân mình.

Như một phương sách cuối cùng bạn có thể gọi là yourQuery.AsEnumerable(), điều này sẽ khiến linq-to-sql dịch truy vấn của bạn thành sql cho đến thời điểm này và thực hiện phần còn lại của công việc ở phía máy khách; sau đó bạn có thể .compile() biểu thức của bạn. Tuy nhiên bạn bị mất những lợi ích hiệu suất của LINQ-to-sql (và biên dịch() chính nó là khá chậm; bất cứ khi nào nó được thực thi, nó gọi là JIT-biên dịch):

return db.Parents 
    .Where(parent => parent.Status == 1) 
    .AsEnumerable() 
    .Where(parent => filter.Compile().Invoke(parent.Child)) 

Cá nhân tôi chỉ muốn xác định sự biểu hiện hai lần, một lần cho con và một lần cho parent.child:

Expression<Func<Child, bool>> filterChild = child => child.Status == 1; 
    Expression<Func<Parent, bool>> filterParent = parent => parent.Child.Status == 1; 

có thể không phải là thanh lịch nhất, nhưng có lẽ dễ dàng hơn để duy trì hơn so với các giải pháp khác

0

Chỉ cần đưa ra với điều này, kiểm tra nếu điều này sẽ làm việc cho bạn

public interface IStatus { public int Status { get; set; } } 
public class Child : IStatus { } 
public class Parent : IStatus 
{public Child Child { get; set; } } 

Func<IStatus, bool> filter = (x) => x.Status == 1; 
var list = Parents.Where(parent => filter(parent) && filter(parent.Child)); 

Hy vọng điều này sẽ hữu ích!

+1

Func không hoạt động với linq-to-sql, nó phải là Biểu thức . Và vì cha mẹ và con có lẽ là các lớp được tạo tự động dựa trên lược đồ db, cho chúng một lớp cơ sở chung không đơn giản như vậy – HugoRune

+0

Giả sử tiêu chí thực sự được chia sẻ một cách đơn giản, đây thực sự là một cách tiếp cận tốt: dưới dạng 'partial', để bạn có thể thêm những thứ như khai báo/triển khai giao diện trong một tệp khác. Vì bạn sẽ cần sử dụng Expression thay vì Func, mã đã cho sẽ không hoạt động như-nhưng, bạn có thể kết hợp chúng bằng các phương thức được hiển thị tại http://stackoverflow.com/questions/1922497/how-do-i- kết hợp-linq-biểu thức-thành-một. –

0

bạn có thể chỉ cần sử dụng các biểu hiện như một hàm để thay thế?

Thay vì:

Expression<Func<Child, bool>> filter = child => child.Status == 1; 

Sử dụng cùng biểu hiện như một hàm tổng quát theo cách này:

Func<Child, bool> filter = child => child.Status == 1; 

Sau đó, bạn sẽ có thể sử dụng chức năng chỉ trong giống như cách bạn đã cố gắng để sử dụng cụm từ:

return db.Parents.Where(parent => parent.Status == 1 && 
            filter(parent.Child)); 
+1

Giải pháp của bạn không dịch sang SQL. –

+0

Trong LINQ to sql, Biểu thức hoạt động, thay vì Func. – Lali

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