Linq: การเพิ่มเงื่อนไขไปยัง where clause conditionally


103

ฉันมีคำถามเช่นนี้

(from u in DataContext.Users
       where u.Division == strUserDiv 
       && u.Age > 18
       && u.Height > strHeightinFeet  
       select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

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

หากต้องทำโดยใช้แบบสอบถาม sql ฉันจะใช้ตัวสร้างสตริงเพื่อต่อท้ายคิวรี strSQL หลัก แต่ที่นี่ใน Linq ฉันคิดได้แค่ใช้เงื่อนไข IF ที่ฉันจะเขียนแบบสอบถามเดียวกันสามครั้งโดยแต่ละบล็อก IF มีเงื่อนไขเพิ่มเติม มีวิธีที่ดีกว่านี้หรือไม่?

คำตอบ:


182

หากคุณไม่เรียกใช้ToList()และการแมปสุดท้ายของคุณกับประเภท DTO คุณสามารถเพิ่มส่วนWhereคำสั่งได้ในขณะที่คุณไปและสร้างผลลัพธ์ในตอนท้าย:

var query = from u in DataContext.Users
   where u.Division == strUserDiv 
   && u.Age > 18
   && u.Height > strHeightinFeet
   select u;

if (useAge)
   query = query.Where(u => u.Age > age);

if (useHeight)
   query = query.Where(u => u.Height > strHeightinFeet);

// Build the results at the end
var results = query.Select(u => new DTO_UserMaster
   {
     Prop1 = u.Name,
   }).ToList();

สิ่งนี้จะยังคงส่งผลให้มีการเรียกไปยังฐานข้อมูลเพียงครั้งเดียวซึ่งจะมีประสิทธิภาพเช่นเดียวกับการเขียนแบบสอบถามในรอบเดียว


1
ฉันต้องใส่เงื่อนไข Where ทั้งหมดในคำสั่ง "var query = .. " หรือไม่
user20358

4
เงื่อนไขที่ตามมาจะรวมเป็นหรือหรือเป็น AND?
Vi100

4
@ vi100 พวกเขาจะเป็นตัวกรองเพิ่มเติมดังนั้นและ
Reed Copsey

ขอบคุณพระเจ้าสำหรับความเรียบง่าย! ฉันเบื่อมากที่เห็นข้อความค้นหา Linq มากกว่า 20 บรรทัดเมื่อข้างต้นอ่านได้ง่ายขึ้นมาก
justanotherdev

ทำไมฉันถึงได้รับข้อผิดพลาดนี้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

19

ฉันมักจะใช้วิธีการผูกมัด แต่มีปัญหาเดียวกัน และนี่คือส่วนขยายที่ฉันใช้

public static IQueryable<T> ConditionalWhere<T>(
        this IQueryable<T> source, 
        Func<bool> condition,
        Expression<Func<T, bool>> predicate)
    {
        if (condition())
        {
            return source.Where(predicate);
        }

        return source;
    }

ช่วยหลีกเลี่ยงโซ่ขาด ยังเหมือนกันConditionalOrderByและConditionalOrderByDescendingเป็นประโยชน์


เป็นประโยชน์ แต่คุณช่วยเพิ่มตัวอย่างของการใช้งานได้ไหม
Suncat2000

1
ควรจะเป็นดังนี้: var fruits = await db.Fruits .ConditionalWhere (() => color! = null, f => f.Color == color) .ConditionalWhere (() => สุก! = null, f => f . สุก == สุก) .ToListAsync ();
Yuriy Granovskiy

4
ใช้งานได้ดี! ขอบคุณ! ฉันยังทำ overload สำหรับเงื่อนไขเป็นค่าบูลีนอย่างง่ายแทนที่จะเป็นฟังก์ชันเพื่อให้ใช้งานง่ายขึ้นซึ่งผู้รับมอบสิทธิ์จะเพิ่มความซับซ้อนที่ไม่จำเป็น ตอนนี้ฉันใช้วิธีการขยายนี้บ่อยครั้งและขอขอบคุณโซลูชันของคุณมาก
Suncat2000

18

ทางเลือกหนึ่ง

bool? age = null

(from u in DataContext.Users
           where u.Division == strUserDiv 
           && (age == null || (age != null && u.Age > age.Value))
           && u.Height > strHeightinFeet  
           select new DTO_UserMaster
           {
             Prop1 = u.Name,
           }).ToList();

หรือคุณสามารถเปลี่ยนไปใช้ไวยากรณ์วิธีการสำหรับ linq และใช้ if เงื่อนไขเพื่อแนบนิพจน์เข้ากับประโยค where


3

เพียงแค่ฉันใช้มันในที่ของฉันเป็นประโยค

    public IList<ent_para> getList(ent_para para){
     db.table1.Where(w=>(para.abc!=""?w.para==para.abc:true==true) && (para.xyz!=""?w.xyz==para.xyz:true==true)).ToList();
}

3

ขึ้นอยู่กับเงื่อนไขบางอย่างเพิ่มเงื่อนไขที่ ...

from u in DataContext.Users
where u.Division == strUserDiv 
&& u.Age != null ? u.Age > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
 select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

2

นี่คือรหัสของฉันที่จะทำสิ่งที่คล้ายกัน นี่เป็นวิธีการบน API บริการเว็บ WCF SOAP ของฉัน

    public FruitListResponse GetFruits(string color, bool? ripe)
    {
        try
        {
            FruitContext db = new FruitContext();
            var query = db.Fruits.Select(f => f);
            if (color != null)
            {
                query = query.Where(f => f.Color == color);
            }
            if (ripe != null)
            {
                query = query.Where(f => f.Ripe == ripe);
            }
            return new FruitListResponse
            {
                Result = query.Select(f => new Fruit { Id = f.FruitId, Name = f.Name }).ToList()
            };
        }
        catch (Exception e)
        {
            return new FruitListResponse { ErrorMessage = e.Message };
        }
    }

แบบสอบถามพื้นฐานSelect(f => f)หมายถึงทุกอย่างโดยทั่วไปและส่วนWhereคำสั่งจะแนบมาด้วยหรือไม่ก็ได้ ขั้นสุดท้ายSelectเป็นทางเลือก ฉันใช้เพื่อแปลงวัตถุแถวฐานข้อมูลให้เป็นวัตถุผลลัพธ์ "ผลไม้"


0

สมมติว่าพารามิเตอร์ต่อไปนี้

Int? Age = 18;

เพียงแค่ใช้&&และ||ผู้ประกอบการตามเงื่อนไขที่เราสามารถมีรุ่นอื่น

(from u in DataContext.Users
where u.Division == strUserDiv 
    && (Age == null || u.Age > Age)
    && (Param1 == null || u.param1 == Param1)
    && u.Height > strHeightinFeet
select new DTO_UserMaster
{
    Prop1 = u.Name,
}).ToList();

เช่นเดียวกับ Param1 คุณสามารถเพิ่มพารามิเตอร์จำนวนเท่าใดก็ได้สำหรับเงื่อนไขการค้นหา


0

ฉันเพิ่งเจอสิ่งนี้กำลังมองหาอย่างอื่น แต่คิดว่าฉันจะใช้แลมด้าเวอร์ชั่น

ขั้นแรกฉันจะสร้างคลาสแบบนี้เพื่อส่งผ่านพารามิเตอร์ไปยังชั้นข้อมูล:

   public class SearchParameters() {
       public int? Age {get; set;}
       public string Division {get;set;}
       etc
    }

จากนั้นในชั้นข้อมูลของฉันจะมีดังนี้:

public IQueryable<User> SearchUsers(SearchParameters params) 
{
    var query = Context.Users;
    if (params.Age.HasValue)
    {
         query = query.Where(u => u.Age == params.Age.Value);
    }
    if (!string.IsNullOrEmpty(params.Division)
    {
        query = query.Where(u => u.Division == params.Division);
    }
    etc
    return query;
}

ในกรณีที่คุณสร้างแบบสอบถามขึ้นอยู่กับคุณ อาจมีเลเยอร์ระหว่างแอปและข้อมูลซึ่งแปลงการแสดงเฉพาะฐานข้อมูลเป็นฐานข้อมูลไม่เชื่อเรื่องพระเจ้า (บางทีคุณอาจค้นหาแหล่งข้อมูลหลายแหล่ง) เลเยอร์นั้นอาจได้รับคิวรีหลายประเภทจากแหล่งข้อมูลเหล่านี้และแมปกับการแสดง POCO ทั่วไปตัวอย่างเช่น


อ๊ะฉันไม่เห็นคำตอบของ John Henckel ความคิดเดียวกัน.
Scott Peterson

0

เพียงเพื่อเพิ่มคำตอบที่ยอมรับข้างต้นที่นี่หากคุณกำลังทำการค้นหาแบบไดนามิกในการรวมให้พิจารณาส่งคืนวัตถุใหม่พร้อมทั้งตาราง (t1, t2) ในแบบสอบถาม linq เริ่มต้นเพื่อให้คุณสามารถเข้าถึงทีละรายการเพื่อทำตามเงื่อนไข ค้นหา.

var query = from t1 in _context.Table1
            join t2 in _context.Table2 on t1.Table1Id equals t2.Table1IdId
            select new { t1, t2 };

        if (!string.IsNullOrEmpty(searchProperty1))
        {
            query = query.Where(collection => collection.t1.TableColumn == searchProperty1);
        }
        if (!string.IsNullOrEmpty(searchProperty2))
        {
            query = query.Where(collection => collection.t2.TableColumn == searchProperty2);
        }
        ....etc.

ฉันได้รับคำตอบที่ฉันกำลังมองหาที่นี่เกี่ยวกับการรวมตารางสองตารางและค้นหาคอลัมน์เฉพาะในตารางใดตารางหนึ่ง

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