2009-04-23 29 views
27

Tôi có một chuỗi có kích thước biến của chuỗi và tôi đang cố lập trình vòng qua mảng và khớp tất cả các hàng trong bảng có cột "Thẻ" chứa ít nhất một trong các chuỗi trong mảng. Dưới đây là một số mã giả:Cách tự động thêm toán tử OR vào mệnh đề WHERE trong LINQ

IQueryable<Songs> allSongMatches = musicDb.Songs; // all rows in the table 

tôi có thể dễ dàng truy vấn lọc bảng này trên một tập hợp cố định các chuỗi, như thế này:

allSongMatches=allSongMatches.Where(SongsVar => SongsVar.Tags.Contains("foo1") || SongsVar.Tags.Contains("foo2") || SongsVar.Tags.Contains("foo3")); 

Tuy nhiên, điều này không làm việc (tôi nhận được lỗi sau: "Một biểu thức lambda có thân xác nhận không thể được chuyển thành cây biểu thức")

allSongMatches = allSongMatches.Where(SongsVar => 
    { 
     bool retVal = false; 
     foreach(string str in strArray) 
     { 
     retVal = retVal || SongsVar.Tags.Contains(str); 
     } 
     return retVal; 
    }); 

Ai có thể cho tôi biết chiến lược chính xác để thực hiện điều này không? Tôi vẫn còn mới với thế giới của LINQ :-)

Trả lời

32

Bạn có thể sử dụng lớp PredicateBuilder:

var searchPredicate = PredicateBuilder.False<Songs>(); 

foreach(string str in strArray) 
{ 
    var closureVariable = str; // See the link below for the reason 
    searchPredicate = 
    searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable)); 
} 

var allSongMatches = db.Songs.Where(searchPredicate); 

LinqToSql strange behaviour

+0

Cảm ơn Mehrdad, giải pháp của bạn bước vào thời gian kỷ lục và làm việc như một nét duyên dáng! Tôi không biết về lớp PredicateBuilder. Thật là một tính năng tiện dụng! Tôi cũng đánh giá cao nhận xét của bạn về việc sử dụng biến tạm thời để giữ các chuỗi ... điều đó sẽ khiến tôi phát điên! Victor –

+0

Bạn được chào đón! –

+0

I Got Confused về biểu thức False để tạo var searchPredicate, Điều này cần phải sai? hoặc bất cứ điều gì khác sẽ phù hợp? (Nói về PredicateBuilder.False ()) – Daniel

0

Hoặc xây dựng một Expression<T> chính mình, hoặc nhìn vào một con đường khác.

Giả sử thẻ có thể là tập hợp các thẻ, bạn có thể sử dụng đóng và tham gia để tìm các kết quả phù hợp. Điều này sẽ tìm thấy bất kỳ bài hát nào có ít nhất một thẻ trong possibleTags:

allSongMatches = allSongMatches.Where(s => (select t from s.Tags 
              join tt from possibleTags 
               on t == tt 
              select t).Count() > 0) 
+0

Hey Richard, tôi đánh giá cao phản hồi của bạn . Đối với một số lý do, VS không thích cú pháp của mã bạn đề xuất ... tôi có thiếu cái gì đó hiển nhiên không? –

+0

Khá có thể có lỗi ... sẽ cần một chút thời gian để kết hợp dữ liệu/kiểm tra khai thác để xác nhận. Nhưng đã thêm mệnh đề chọn vào biểu thức hiểu bên trong ... được yêu cầu (oops). – Richard

0

Có một phương pháp khác, dễ dàng hơn sẽ thực hiện việc này. Blog của ScottGu liệt kê một thư viện LINQ động mà tôi đã thấy rất hữu ích trong quá khứ. Về cơ bản, nó tạo ra các truy vấn từ một chuỗi bạn vượt qua trong Dưới đây là một ví dụ của mã bạn muốn viết:.

Dim Northwind As New NorthwindDataContext 

Dim query = Northwind.Products _ 
        .Where("CategoryID=2 AND UnitPrice>3") _ 
        .OrderBy("SupplierId") 

Gridview1.DataSource = query 
Gridview1.DataBind() 

Thông tin thêm có thể được tìm thấy tại blog của ScottGu here.

+0

không chắc chắn lý do tại sao này -1, trông một câu trả lời hợp pháp – piris

1

Gần đây tôi đã tạo ra một phương pháp khuyến nông để tạo chuỗi tìm kiếm cũng cho phép tìm kiếm OR. Viết blog về here

Tôi cũng tạo ra nó như là một gói NuGet mà bạn có thể cài đặt:

http://www.nuget.org/packages/NinjaNye.SearchExtensions/

Sau khi cài đặt bạn sẽ có thể làm như sau

var result = db.Songs.Search(s => s.Tags, strArray); 

Nếu bạn muốn tạo phiên bản của riêng bạn để cho phép ở trên, bạn sẽ cần phải làm như sau:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms) 
    { 
     if (!searchTerms.Any()) 
     { 
      return source; 
     } 

     Expression orExpression = null; 
     foreach (var searchTerm in searchTerms) 
     { 
      //Create expression to represent x.[property].Contains(searchTerm) 
      var searchTermExpression = Expression.Constant(searchTerm); 
      var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression); 

      orExpression = BuildOrExpression(orExpression, containsExpression); 
     } 

     var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters); 
     return source.Where(completeExpression); 
    } 

    private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd) 
    { 
     if (existingExpression == null) 
     { 
      return expressionToAdd; 
     } 

     //Build 'OR' expression for each property 
     return Expression.OrElse(existingExpression, expressionToAdd); 
    } 
} 

Ngoài ra, hãy nhìn vào các dự án github cho NinjaNye.SearchExtensions như thế này có các tùy chọn khác và has been refactored hơi để cho phép các kết hợp khác

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