2013-03-30 43 views
7

Tôi đã thấy rằng có thể thêm các phương thức được biên dịch cùng nhau.Mục đích của việc thêm các phương thức Func đã biên dịch cùng nhau là gì?

Expression<Func<Customer, bool>> ln = c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase); 
Expression<Func<Customer, bool>> fn = c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase); 
Expression<Func<Customer, bool>> fdob = c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd")); 

var filter = ln.Compile() + fn.Compile() + fdob.Compile(); 

Bạn có hiểu không?

tôi sẽ có ý định sử dụng bộ lọc ở vị trí của một biểu thức lambda để lọc một kho lưu trữ của khách hàng:

IEnumerable<Customer> customersFound = _repo.Customers.Where(filter); 

Tùy thuộc vào logic kinh doanh, tôi có thể có hoặc không thêm ba phương pháp biên soạn với nhau, nhưng chọn và chọn, và có thể thêm nhiều phương thức được biên dịch khi tôi đi.

Bất cứ ai có thể giải thích nếu thêm chúng lại với nhau sẽ xây dựng chuỗi truy vấn hay không? Bất cứ ai cũng có một gợi ý tốt hơn?

Tôi có thể chuỗi các câu lệnh "Where" và sử dụng các biểu thức lambda thông thường, nhưng tôi bị hấp dẫn bởi những gì bạn có thể thu được từ việc biên dịch các phương thức và thêm chúng lên!

Trả lời

3

Đó là một nỗ lực sáng tạo để các vị từ chuỗi và quá xấu nó không hoạt động. Lý do tại sao đã được giải thích một cách xuất sắc bởi Lasse và Nicholas (+2). Nhưng bạn cũng hỏi:

Có ai có đề xuất tốt hơn không?

Hạn chế các biểu thức biên soạn là họ không biểu hiện bất kỳ hơn và do đó, không thể được sử dụng với IQueryable s được hỗ trợ bởi các nhà cung cấp truy vấn SQL (như LINQ to SQL hay LINQ to Entities). Tôi giả sử _repo.Customers là một số IQueryable.

Nếu bạn muốn biểu hiện chuỗi động, LINQKit's PredicateBuilder là một công cụ tuyệt vời để do this.

var pred = Predicate.True<Customer>(); 

pred = pred.And(c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase); 
pred = pred.And(c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase); 
pred = pred.And(c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd")); 

var customersFound = _repo.Customers.Where(pred.Expand()); 

Đây là những gì Expand dành cho:

đường ống xử lý truy vấn Entity Framework không thể xử lý các biểu gọi, đó là lý do tại sao bạn cần phải gọi AsExpandable trên đối tượng đầu tiên trong truy vấn. Bằng cách gọi AsExpandable, bạn kích hoạt lớp khách truy cập biểu thức LINQKit, thay thế các biểu thức gọi bằng các cấu trúc đơn giản mà Entity Framework có thể hiểu được.

Hoặc: nếu không có nó một biểu thức là Invoke d, gây ra một ngoại lệ trong EF:

Các LINQ nút biểu gõ 'Gọi' không được hỗ trợ trong LINQ to Entities.

BTW. Nếu bạn sử dụng LINQ để sql/thực thể bạn cũng có thể sử dụng c.lastname == _customer.lastName vì nó được dịch sang SQL và cơ sở dữ liệu collation sẽ xác định trường hợp nhạy cảm.

+0

Cảm ơn Gert, rất hữu ích. Tôi nghĩ rằng tôi sẽ cài đặt LinqKit vào dự án của tôi và cung cấp cho nó một đi. Đó là một sự xấu hổ thêm các đại biểu với nhau không xây dựng một bộ lọc như tôi đã hy vọng, nhưng LinqKit trông giống như một giải pháp tốt, và nó trông khá sạch sẽ cú pháp. Dự án của tôi đang sử dụng EF5 và SQL2012 trong trường hợp ai đó đang tự hỏi. –

+0

Lasse và Nicholas giải thích về việc thêm các đại biểu multicast cùng nhau, nhưng giải pháp đề xuất của Gert trả lời tốt nhất: "Bất cứ ai cũng có một đề xuất tốt hơn?". Tôi đã thực hiện một giải pháp bằng cách sử dụng LinqKit như được đề xuất. Cảm ơn nhiều) –

7

Đó là một tác dụng phụ của thực tế là phương pháp Compile trả về đại biểu.

Trong nội bộ, đại biểu là đại biểu đa phương tiện, nó có thể liên quan đến nhiều tham chiếu xích đến phương pháp.

+ trong trường hợp này chỉ là một cách nhanh chóng để kết nối chúng lại với nhau. Bạn có thể đọc thêm về cách kết hợp các đại biểu trong How to: Combine Delegates trên MSDN.

Dưới đây là một chương trình LINQPad đó chứng tỏ:

void Main() 
{ 
    var filter = GetX() + GetY() + GetZ(); 
    filter().Dump(); 
} 

public int X() { Debug.WriteLine("X()"); return 1; } 
public int Y() { Debug.WriteLine("Y()"); return 2; } 
public int Z() { Debug.WriteLine("Z()"); return 3; } 

public Func<int> GetX() { return X; } 
public Func<int> GetY() { return Y; } 
public Func<int> GetZ() { return Z; } 

Output:

X() 
Y() 
Z() 
3 

Lưu ý rằng nó như vậy dường như chỉ bỏ qua các giá trị trả về từ các cuộc gọi phương pháp đầu tiên, và chỉ trả về trước, mặc dù nó hoàn toàn gọi các phương thức trước đó.

Lưu ý rằng đoạn code trên là tương tự như sau:

void Main() 
{ 
    Func<int> x = X; 
    Func<int> y = Y; 
    Func<int> z = Z; 

    var filter = x + y + z; 
    filter().Dump(); 
} 

public int X() { Debug.WriteLine("X()"); return 1; } 
public int Y() { Debug.WriteLine("Y()"); return 2; } 
public int Z() { Debug.WriteLine("Z()"); return 3; } 

Câu trả lời ngắn gọn là như vậy mà không có, điều này không làm những gì bạn muốn nó làm.

+0

Cảm ơn bạn Lasse. Bằng chứng tôi đã thấy chỉ cho thấy phương pháp cuối cùng trong chuỗi được sử dụng để lọc, nhưng tôi không chắc chắn 100% của ruột. –

5

Khi bạn gọi Compile(), bạn đang tạo phiên bản loại Func<Customer, bool>, là đại biểu.

Đại biểu quá tải operator+ để trả lại Đại biểu đa phương tiện, đó là những gì đang xảy ra ở đây.

Xem MSDN: How to: Combine Delegates (Multicast Delegates)

Vì vậy, không có - thêm chúng với nhau sẽ không xây dựng một chuỗi truy vấn.


Nếu điều này là dành cho EF, bạn muốn sử dụng Cây biểu thức, chứ không phải Lambdas.

Bạn có thể tạo và chỉnh sửa Trees Biểu hiện bằng cách sử dụng System.Linq.Expressions namespace:

MSDN: System.Linq.Expressions Namespace

MSDN: How to: Use Expression Trees to Build Dynamic Queries

+0

Cảm ơn bạn Nicholas, tôi sẽ theo dõi các liên kết của bạn. Tôi đã thử tìm kiếm câu trả lời của google, nhưng tôi cho rằng tôi đã tìm kiếm với các thuật ngữ sai. Nhiều nghĩa vụ. –

+0

@IanDangerRobertson Bạn được chào đón và cảm ơn vì một câu hỏi thú vị :) –

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