2008-08-14 21 views
78

Chúng tôi đang làm việc trên Trình xem nhật ký. Việc sử dụng sẽ có tùy chọn để lọc theo người dùng, mức độ nghiêm trọng, v.v. Trong các ngày Sql tôi muốn thêm vào chuỗi truy vấn, nhưng tôi muốn làm điều đó với LINQ. Làm thế nào tôi có thể thêm điều kiện ở đâu?Truy vấn LINQ có điều kiện

Trả lời

138

nếu bạn chỉ muốn lọc nếu tiêu chí nhất định được thông qua, làm một cái gì đó như thế này

var logs = from log in context.Logs 
      select log; 

if (filterBySeverity) 
    logs = logs.Where(p => p.Severity == severity); 

if (filterByUser) 
    logs = logs.Where(p => p.User == user); 

Làm như vậy theo cách này sẽ cho phép cây Biểu hiện của bạn là chính xác những gì bạn muốn. Bằng cách đó, SQL được tạo ra sẽ chính xác những gì bạn cần và không có gì ít hơn.

+2

Xin chào Bạn có bất kỳ đề xuất nào về việc thực hiện mệnh đề ORs thay vì AND không ..? –

+1

Vâng ... hơi khó để làm. Tốt nhất tôi đã nhìn thấy là thông qua các mô hình đặc điểm kỹ thuật và kéo vị từ vào đặc điểm kỹ thuật và sau đó gọi specification.Or (someOtherSpecification). Về cơ bản bạn phải viết cây biểu hiện của riêng bạn một chút. Ví dụ mã và giải thích ở đây: http://codeinsanity.com/archive/2008/08/13/implementing-repository-and-specification-patterns-using-linq.aspx –

+0

Tôi có một câu hỏi ngu ngốc, Nếu các bản ghi này đang được mua lại từ cơ sở dữ liệu, chúng tôi có nhận được tất cả nhật ký và sau đó lọc chúng trong bộ nhớ không? Nếu vậy thì làm thế nào tôi có thể vượt qua các điều kiện để cơ sở dữ liệu –

0

Chỉ cần sử dụng C# 's & & điều hành:

var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical") 

Edit: Ah, cần phải đọc kỹ lưỡng hơn. Bạn muốn biết cách có điều kiện thêm mệnh đề bổ sung. Trong trường hợp đó, tôi không biết. :) Những gì tôi có thể làm là chỉ cần chuẩn bị một số truy vấn, và thực hiện một trong những quyền, tùy thuộc vào những gì tôi đã kết thúc cần.

0

Bạn có thể sử dụng một phương pháp bên ngoài:

var results = 
    from rec in GetSomeRecs() 
    where ConditionalCheck(rec) 
    select rec; 

... 

bool ConditionalCheck(typeofRec input) { 
    ... 
} 

này sẽ làm việc, nhưng không thể được chia nhỏ thành các cây biểu hiện, có nghĩa là LINQ to SQL sẽ chạy mã kiểm tra chống lại mọi kỷ lục.

Hoặc:

var results = 
    from rec in GetSomeRecs() 
    where 
     (!filterBySeverity || rec.Severity == severity) && 
     (!filterByUser|| rec.User == user) 
    select rec; 

Điều đó có thể làm việc trong cây biểu hiện, có nghĩa là LINQ to SQL sẽ được tối ưu hóa.

0

Vâng, những gì tôi nghĩ là bạn có thể đặt điều kiện lọc vào một danh sách chung của vị từ:

var list = new List<string> { "me", "you", "meyou", "mow" }; 

    var predicates = new List<Predicate<string>>(); 

    predicates.Add(i => i.Contains("me")); 
    predicates.Add(i => i.EndsWith("w")); 

    var results = new List<string>(); 

    foreach (var p in predicates) 
     results.AddRange(from i in list where p.Invoke(i) select i);    

Điều đó dẫn tới một danh sách có chứa "tôi", "meyou", và "cắt".

Bạn có thể tối ưu hóa điều đó bằng cách thực hiện việc tìm kiếm với các vị từ trong một hàm hoàn toàn khác HOẶC tất cả các biến vị ngữ.

1

Nó không phải là điều đẹp nhất nhưng bạn có thể sử dụng một biểu thức lambda và vượt qua các điều kiện của bạn tùy chọn. Trong TSQL tôi làm được rất nhiều những điều sau đây để làm cho thông số tùy chọn:

ĐÂU Dòng = @FieldVar HOẶC @FieldVar IS NULL

Bạn có thể lặp lại trong các phong cách tương tự với một lambda sau (một ví dụ kiểm tra xác thực):

MyDataContext db = new MyDataContext();

trống RunQuery (string param1, param2 chuỗi, int? Param3) {

Func checkUser = user =>

((param1.Length> 0)? Dùng.Param1 == param1: 1 == 1) & &

((param2.Length> 0) user.Param2 == param2: 1 == 1) & &

(! (Param3 = null) ? user.Param3 == param3: 1 == 1);

Người dùng được tìm thấyUser = db.Users.SingleOrDefault (checkUser);

}

13

Khi nói đến LINQ có điều kiện, tôi rất thích mô hình bộ lọc và đường ống.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Về cơ bản, bạn tạo phương pháp mở rộng cho từng trường hợp bộ lọc có trong IQueryable và tham số.

public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id) 
{ 
    return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query; 
} 
3

Một tùy chọn khác sẽ là sử dụng một cái gì đó giống như PredicateBuilder đã thảo luận here. Nó cho phép bạn viết mã như sau:

var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone"); 

var classics = Product.ContainsInDescription ("Nokia", "Ericsson") 
        .And (Product.IsSelling()); 

var query = from p in Data.Products.Where (newKids.Or (classics)) 
      select p; 

Lưu ý rằng tôi chỉ có này để làm việc với LINQ 2 SQL. EntityFramework không thực hiện Expression.Invoke, được yêu cầu cho phương thức này hoạt động. Tôi có một câu hỏi liên quan đến vấn đề này here.

+0

Đây là một phương pháp tuyệt vời cho những người sử dụng một lớp logic kinh doanh trên đầu trang của kho lưu trữ của họ cùng với một công cụ như AutoMapper để ánh xạ giữa truyền dữ liệu đối tượng và mô hình Thực thể. Sử dụng trình tạo biến vị ngữ sẽ cho phép bạn tự động sửa đổi IQueryable của mình trước khi gửi nó tới AutoMapper để làm phẳng, tức là đưa danh sách vào bộ nhớ. Lưu ý rằng nó cũng hỗ trợ Entity Framework. – chrisjsherm

1

Tôi đã có một yêu cầu tương tự gần đây và cuối cùng tìm thấy điều này trong MSDN. CSharp Samples for Visual Studio 2008

Các lớp bao gồm trong mẫu DynamicQuery của tải cho phép bạn tạo các truy vấn năng động trong thời gian chạy theo định dạng sau:

var query = 
db.Customers. 
Where("City = @0 and Orders.Count >= @1", "London", 10). 
OrderBy("CompanyName"). 
Select("new(CompanyName as Name, Phone)"); 

Sử dụng bạn này có thể xây dựng một chuỗi truy vấn tự động trong thời gian chạy và vượt qua nó vào đâu) phương pháp (:

string dynamicQueryString = "City = \"London\" and Order.Count >= 10"; 
var q = from c in db.Customers.Where(queryString, null) 
     orderby c.CompanyName 
     select c; 
19

tôi đã kết thúc bằng một câu trả lời tương tự như Daren, nhưng với một giao diện IQueryable:

IQueryable<Log> matches = m_Locator.Logs; 

// Users filter 
if (usersFilter) 
    matches = matches.Where(l => l.UserName == comboBoxUsers.Text); 

// Severity filter 
if (severityFilter) 
    matches = matches.Where(l => l.Severity == comboBoxSeverity.Text); 

Logs = (from log in matches 
     orderby log.EventTime descending 
     select log).ToList(); 

Điều đó tạo nên truy vấn trước khi nhấn vào cơ sở dữ liệu. Lệnh sẽ không chạy cho đến khi .ToList() ở cuối.

21

Nếu bạn cần phải lọc dựa trên một danh sách/Array sử dụng như sau:

public List<Data> GetData(List<string> Numbers, List<string> Letters) 
    { 
     if (Numbers == null) 
      Numbers = new List<string>(); 

     if (Letters == null) 
      Letters = new List<string>(); 

     var q = from d in database.table 
       where (Numbers.Count == 0 || Numbers.Contains(d.Number)) 
       where (Letters.Count == 0 || Letters.Contains(d.Letter)) 
       select new Data 
       { 
        Number = d.Number, 
        Letter = d.Letter, 
       }; 
     return q.ToList(); 

    } 
+3

Đây là câu trả lời đúng nhất và chính xác nhất. Điều kiện || chỉ so sánh phần đầu tiên và bỏ qua phần thứ hai nếu phần đầu tiên là đúng ... độc đáo được thực hiện! –

+0

Cấu trúc này bao gồm phần 'hoặc' của biểu thức trong truy vấn SQL được tạo. Câu trả lời được chấp nhận sẽ tạo ra các báo cáo hiệu quả hơn. Tùy thuộc vào tối ưu hóa của nhà cung cấp dữ liệu, tất nhiên. LINQ-to-SQL có thể có tối ưu hóa tốt hơn, nhưng LINQ-to-Entities thì không. – Suncat2000

3

Việc làm này:

bool lastNameSearch = true/false; // depending if they want to search by last name, 

có này trong báo cáo where:

where (lastNameSearch && name.LastNameSearch == "smith") 

có nghĩa là khi truy vấn cuối cùng được tạo, nếu lastNameSearchfalse truy vấn sẽ hoàn toàn bỏ qua bất kỳ SQL nào cho tìm kiếm tên cuối cùng.

+0

Phụ thuộc vào nhà cung cấp dữ liệu. LINQ-to-Entities không tối ưu hóa nó tốt. – Suncat2000