แบบสอบถาม Linq ตามเงื่อนไข


92

เรากำลังดำเนินการเกี่ยวกับ Log Viewer การใช้งานจะมีตัวเลือกในการกรองตามผู้ใช้ความรุนแรงและอื่น ๆ ในวันที่ Sql ฉันจะเพิ่มสตริงการสืบค้น แต่ฉันต้องการทำด้วย Linq ฉันจะเพิ่ม where-clauses ตามเงื่อนไขได้อย่างไร

คำตอบ:


156

หากคุณต้องการกรองเฉพาะเมื่อผ่านเกณฑ์บางอย่างให้ทำสิ่งนี้

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);

การทำเช่นนี้จะทำให้ Expression tree ของคุณตรงตามที่คุณต้องการ ด้วยวิธีนี้ SQL ที่สร้างขึ้นจะเป็นสิ่งที่คุณต้องการและไม่มีอะไรน้อย


2
สวัสดีคุณมีคำแนะนำในการสร้าง where clauses ORs แทน ANDs .. ?
Jon H

1
ใช่ ... มันยากที่จะทำ สิ่งที่ดีที่สุดที่ฉันเคยเห็นคือผ่านรูปแบบข้อกำหนดและการดึงเพรดิเคตเข้าไปในข้อกำหนดจากนั้นจึงเรียกข้อกำหนดหรือ (someOtherSpecification) โดยทั่วไปคุณต้องเขียนแผนภูมินิพจน์ของคุณเองสักหน่อย ตัวอย่างโค้ดและคำอธิบายที่นี่codeinsanity.com/archive/2008/08/13/…
Darren Kopp

ฉันมีคำถามโง่ ๆ ว่าถ้าบันทึกเหล่านี้ได้มาจากฐานข้อมูลเราจะได้รับบันทึกทั้งหมดแล้วกรองในหน่วยความจำหรือไม่? ถ้าเป็นเช่นนั้นฉันจะส่งเงื่อนไขไปยังฐานข้อมูลได้อย่างไร
Ali Umair

ไม่ได้กรองไว้ในหน่วยความจำ มันสร้างแบบสอบถามและส่งเงื่อนไขทั้งหมดในฐานข้อมูล (อย่างน้อยสำหรับผู้ให้บริการ linq-to-x ส่วนใหญ่)
Darren Kopp

ได้รับข้อผิดพลาดนี้LINQ 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

23

หากคุณต้องการกรองฐานใน List / Array ให้ใช้สิ่งต่อไปนี้:

    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
นี่เป็นคำตอบที่ดีที่สุดและถูกต้องที่สุด เงื่อนไข || เปรียบเทียบเฉพาะส่วนแรกและข้ามส่วนที่สองหากส่วนแรกเป็นจริง ... ทำได้ดีมาก!
Serj Sagan

1
โครงสร้างนี้มีส่วน 'หรือ' ของนิพจน์ในแบบสอบถาม SQL ที่สร้างขึ้น คำตอบที่ยอมรับจะสร้างข้อความที่มีประสิทธิภาพมากขึ้น ขึ้นอยู่กับการเพิ่มประสิทธิภาพของผู้ให้บริการข้อมูลแน่นอน LINQ-to-SQL อาจมีการเพิ่มประสิทธิภาพที่ดีกว่า แต่ LINQ-to-Entities ไม่มี
Suncat2000

20

ฉันจบลงด้วยคำตอบที่คล้ายกับของ Daren แต่มีอินเทอร์เฟซ 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();

ที่สร้างแบบสอบถามก่อนที่จะกดฐานข้อมูล คำสั่งจะไม่ทำงานจนกว่า. toList () ในตอนท้าย


14

เมื่อพูดถึง linq แบบมีเงื่อนไขฉันชอบฟิลเตอร์และรูปแบบท่อมาก
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

โดยทั่วไปคุณจะสร้างวิธีการขยายสำหรับแต่ละกรณีตัวกรองที่ใช้ใน IQueryable และพารามิเตอร์

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

8

ฉันแก้ไขสิ่งนี้ด้วยวิธีการขยายเพื่ออนุญาตให้ LINQ เปิดใช้งานตามเงื่อนไขกลางนิพจน์ที่คล่องแคล่ว สิ่งนี้ทำให้ไม่จำเป็นต้องแยกนิพจน์ด้วยifคำสั่ง

.If() วิธีการขยาย:

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

สิ่งนี้ช่วยให้คุณทำสิ่งนี้:

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

นี่คือIEnumerable<T>เวอร์ชันที่จะจัดการกับนิพจน์ LINQ อื่น ๆ ส่วนใหญ่:

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

4

อีกตัวเลือกหนึ่งที่จะใช้สิ่งที่ต้องการ PredicateBuilder กล่าวถึงที่นี่ ช่วยให้คุณสามารถเขียนโค้ดดังต่อไปนี้:

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;

โปรดทราบว่าฉันมีเพียงสิ่งนี้เพื่อทำงานกับ Linq 2 SQL EntityFramework ไม่ได้ใช้ Expression.Invoke ซึ่งจำเป็นสำหรับวิธีนี้ในการทำงาน ฉันมีคำถามเกี่ยวกับเรื่องนี้ที่นี่


นี่เป็นวิธีการที่ยอดเยี่ยมสำหรับผู้ที่ใช้ Business Logic Layer ที่ด้านบนของที่เก็บพร้อมกับเครื่องมือเช่น AutoMapper เพื่อแมประหว่างอ็อบเจ็กต์การถ่ายโอนข้อมูลและโมเดลเอนทิตี การใช้ตัวสร้างเพรดิเคตจะช่วยให้คุณสามารถปรับเปลี่ยน IQueryable ของคุณแบบไดนามิกก่อนที่จะส่งไปยัง AutoMapper เพื่อทำให้รายการแบนลงเช่นการนำรายการเข้าสู่หน่วยความจำ โปรดทราบว่ายังสนับสนุน Entity Framework
chrisjsherm

3

ทำสิ่งนี้:

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

มีสิ่งนี้ในwhereคำสั่ง:

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

หมายความว่าเมื่อมีการสร้างแบบสอบถามสุดท้ายหากlastNameSearchเป็นfalseแบบสอบถามจะละเว้น SQL ทั้งหมดสำหรับการค้นหานามสกุล


ขึ้นอยู่กับผู้ให้บริการข้อมูล LINQ-to-Entities ไม่ได้ปรับให้เหมาะสม
Suncat2000

1

ไม่ใช่สิ่งที่สวยที่สุด แต่คุณสามารถใช้นิพจน์แลมบ์ดาและส่งผ่านเงื่อนไขของคุณได้ ใน TSQL ฉันทำสิ่งต่อไปนี้มากมายเพื่อให้พารามิเตอร์เป็นทางเลือก:

WHERE Field = @FieldVar หรือ @FieldVar เป็นโมฆะ

คุณสามารถทำซ้ำสไตล์เดียวกันด้วยแลมด้าต่อไปนี้ (ตัวอย่างการตรวจสอบการพิสูจน์ตัวตน):

MyDataContext db = MyDataContext ใหม่ ();

เป็นโมฆะ RunQuery (สตริง param1, สตริง param2, int? param3) {

Func checkUser = ผู้ใช้ =>

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

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

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

ผู้ใช้ foundUser = db.Users.SingleOrDefault (checkUser);

}


1

เมื่อเร็ว ๆ นี้ฉันมีข้อกำหนดที่คล้ายกันและในที่สุดก็พบสิ่งนี้ใน MSDN CSharp ตัวอย่างสำหรับ Visual Studio 2008

คลาสที่รวมอยู่ในตัวอย่าง DynamicQuery ของการดาวน์โหลดช่วยให้คุณสร้างแบบสอบถามแบบไดนามิกที่รันไทม์ในรูปแบบต่อไปนี้:

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

การใช้สิ่งนี้คุณสามารถสร้างสตริงการสืบค้นแบบไดนามิกที่รันไทม์และส่งผ่านไปยังเมธอด 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

คุณสามารถสร้างและใช้วิธีการขยายนี้

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

0

เพียงใช้ตัวดำเนินการ && ของ C #:

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

แก้ไข: อาต้องอ่านอย่างละเอียดมากขึ้น ที่คุณอยากรู้วิธีการเงื่อนไขเพิ่มคำสั่งเพิ่มเติม ในกรณีนั้นฉันไม่รู้ :) สิ่งที่ฉันควรทำก็แค่เตรียมแบบสอบถามหลาย ๆ ข้อและดำเนินการให้ถูกต้องขึ้นอยู่กับสิ่งที่ฉันต้องการ


0

คุณสามารถใช้วิธีการภายนอก:

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

...

bool ConditionalCheck( typeofRec input ) {
    ...
}

สิ่งนี้ใช้ได้ แต่ไม่สามารถแบ่งออกเป็นแผนภูมินิพจน์ได้ซึ่งหมายความว่า Linq to SQL จะรันโค้ดตรวจสอบกับทุกระเบียน

อีกทางหนึ่ง:

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

สิ่งนี้อาจใช้ได้ในแผนภูมินิพจน์ซึ่งหมายความว่า Linq เป็น SQL จะได้รับการปรับให้เหมาะสม


0

สิ่งที่ฉันคิดคือคุณสามารถใส่เงื่อนไขการกรองลงในรายการทั่วไปของ Predicates:

    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);               

ซึ่งส่งผลให้รายการมี "ฉัน" "ฉัน" และ "ตัดหญ้า"

คุณสามารถปรับให้เหมาะสมได้โดยทำ foreach กับเพรดิเคตในฟังก์ชันที่แตกต่างกันโดยสิ้นเชิงที่ ORs เพรดิเคต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.