Truy vấn Linq có điều kiện


92

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 những ngày Sql, tôi sẽ thêm vào chuỗi truy vấn, nhưng tôi muốn làm điều đó với Linq. Làm cách nào để thêm mệnh đề where-có điều kiện?

Câu trả lời:


156

nếu bạn chỉ muốn lọc nếu một số tiêu chí nhất định được thông qua, hãy làm 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 thức của bạn chính xác như những gì bạn muốn. Bằng cách đó, SQL được tạo ra sẽ chính xác là những gì bạn cần và không hơn kém.


2
Xin chào Bạn có bất kỳ đề xuất nào để tạo mệnh đề where OR thay vì ANDs ..?
Jon H

1
Phải ... hơi khó để làm. Điều tốt nhất tôi đã thấy là thông qua mô hình đặc tả và kéo vị từ vào đặc tả và sau đó gọi đặc tả. Hoặc (someOtherSpecification). Về cơ bản bạn phải viết cây biểu thức của riêng bạn một chút. Mã ví dụ và giải thích ở đây: codeinsanity.com/archive/2008/08/13/…
Darren Kopp

Tôi có một câu hỏi ngu ngốc, Nếu những bản ghi này đang được lấy từ cơ sở dữ liệu, liệu chúng ta có đang nhận được tất cả các bản ghi 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ể chuyển các điều kiện vào cơ sở dữ liệu
Ali Umair

nó không lọc chúng trong bộ nhớ. nó đang xây dựng một truy vấn và gửi tất cả các điều kiện trong cơ sở dữ liệu (ít nhất là đối với hầu hết các nhà cung cấp linq-to-x)
Darren Kopp

gặp lỗi nàyLINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Ali Umair

22

Nếu bạn cần lọc cơ sở trên Danh sách / Mảng, hãy 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à đúng 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 thực hiện tốt!
Serj Sagan

1
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 câu lệnh hiệu quả hơn. Tất nhiên, tùy thuộc vào tối ưu hóa của nhà cung cấp dữ liệu. LINQ-to-SQL có thể tối ưu hóa tốt hơn, nhưng LINQ-to-Entities thì không.
Suncat2000

20

Tôi đã kết thúc bằng một câu trả lời tương tự như câu trả lời của Daren, nhưng với 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 đó xây dựng truy vấn trước khi nhấn cơ sở dữ liệu. Lệnh sẽ không chạy cho đến khi kết thúc .ToList ().


14

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

Về cơ bản, bạn tạo một phương thức mở rộng cho mỗi trường hợp bộ lọc nhận IQueryable và một 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;
}

8

Tôi đã giải quyết vấn đề này bằng một phương thức mở rộng để cho phép LINQ được bật có điều kiện ở giữa một biểu thức trôi chảy. Điều này loại bỏ sự cần thiết phải chia nhỏ biểu thức vớiif câu lệnh.

.If() phương thức mở rộng:

public static IQueryable<TSource> If<TSource>(
        this IQueryable<TSource> source,
        bool condition,
        Func<IQueryable<TSource>, IQueryable<TSource>> branch)
    {
        return condition ? branch(source) : source;
    }

Điều này cho phép bạn làm điều này:

return context.Logs
     .If(filterBySeverity, q => q.Where(p => p.Severity == severity))
     .If(filterByUser, q => q.Where(p => p.User == user))
     .ToList();

Đây cũng là một IEnumerable<T>phiên bản sẽ xử lý hầu hết các biểu thức LINQ khác:

public static IEnumerable<TSource> If<TSource>(
    this IEnumerable<TSource> source,
    bool condition,
    Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
    {
        return condition ? branch(source) : source;
    }

4

Một tùy chọn khác sẽ là sử dụng một cái gì đó giống như PredicateBuilder được thảo luận ở đây . 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ó điều này để làm việc với Linq 2 SQL. EntityFramework không triển khai Expression.Invoke, được yêu cầu để 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 ở đây .


Đây là một phương pháp tuyệt vời cho những người sử dụng Lớp Logic Kinh doanh trên đầu kho của họ cùng với một công cụ như AutoMapper để ánh xạ giữa các đối tượng truyền dữ liệu và các mô hình Thực thể. Sử dụng trình tạo vị từ sẽ cho phép bạn tự động sửa đổi IQueryable của mình trước khi gửi nó đến 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

3

Làm điều này:

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

có điều này trong wheretuyên bố:

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

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


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 như vậy.
Suncat2000,

1

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

Trường WHERE = @FieldVar HOẶC @FieldVar LÀ KHÔNG CÓ

Bạn có thể sao chép cùng một kiểu với lambda sau (ví dụ về kiểm tra xác thực):

MyDataContext db = new MyDataContext ();

void RunQuery (string param1, string param2, int? param3) {

Func checkUser = user =>

((param1.Length> 0)? user.Param1 == param1: 1 == 1) &&

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

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

Người dùng tìm thấy người dùng = db.Users.SingleOrDefault (người dùng kiểm tra);

}


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 của anh ấy. Mẫu CSharp cho Visual Studio 2008

Các lớp có trong mẫu DynamicQuery của bản tải xuống cho phép bạn tạo truy vấn động trong thời gian chạy ở đị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 điều này, bạn có thể xây dựng một chuỗi truy vấn động trong thời gian chạy và chuyển nó vào phương thức Where ():

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

1

Bạn có thể tạo và sử dụng phương pháp mở rộng này

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}

0

Chỉ cần sử dụng toán tử && của C #:

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

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


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 ) {
    ...
}

Điều này sẽ hoạt động, nhưng không thể được chia thành cây biểu thức, có nghĩa là Linq to SQL sẽ chạy mã kiểm tra đối với mọi bản ghi.

Ngoài ra:

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

Điều đó có thể hoạt động trong cây biểu thức, nghĩa là Linq to SQL sẽ được tối ưu hóa.


0

Chà, điều tôi nghĩ là bạn có thể đặt các điều kiện lọc vào một danh sách chung các Dự đoán:

    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 đến một danh sách có chứa "me", "meyou" và "mow".

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.