ลำดับไม่มีองค์ประกอบที่ตรงกัน


113

ฉันมีแอปพลิเคชัน asp.net ซึ่งฉันใช้ linq สำหรับการจัดการข้อมูล ขณะทำงานฉันได้รับข้อยกเว้น "ลำดับไม่มีองค์ประกอบที่ตรงกัน"

if (_lstAcl.Documents.Count > 0)
{
    for (i = 0; i <= _lstAcl.Documents.Count - 1; i++)
    {
        string id = _lstAcl.Documents[i].ID.ToString();                           
        var documentRow = _dsACL.Documents.First(o => o.ID == id);
        if (documentRow !=null)
        {

            _lstAcl.Documents[i].Read = documentRow.Read;
            _lstAcl.Documents[i].ReadRule = documentRow.ReadRule;

            _lstAcl.Documents[i].Create= documentRow.Create;
            _lstAcl.Documents[i].CreateRule = documentRow.CreateRule;

            _lstAcl.Documents[i].Update = documentRow.Update;
            _lstAcl.Documents[i].UpdateRule = documentRow.UpdateRule;

            _lstAcl.Documents[i].Delete = documentRow.Delete;
            _lstAcl.Documents[i].DeleteRule = documentRow.DeleteRule;
        }
    }
}

คำตอบ:


221

ฉันคาดว่ามันเป็นบรรทัดนี้ที่ทำให้เกิดข้อยกเว้น:

var documentRow = _dsACL.Documents.First(o => o.ID == id)

First()จะทำให้เกิดข้อยกเว้นหากไม่พบองค์ประกอบที่ตรงกัน เนื่องจากคุณกำลังทดสอบ null ทันทีหลังจากนั้นดูเหมือนว่าคุณต้องการFirstOrDefault()ซึ่งจะคืนค่าเริ่มต้นสำหรับประเภทองค์ประกอบ (ซึ่งเป็นค่าว่างสำหรับประเภทการอ้างอิง) หากไม่พบรายการที่ตรงกัน:

var documentRow = _dsACL.Documents.FirstOrDefault(o => o.ID == id)

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

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

var query = from target in _lstAcl.Documents
            join source in _dsAcl.Document
            where source.ID.ToString() equals target.ID
            select new { source, target };
foreach (var pair in query)
{
    target.Read = source.Read;
    target.ReadRule = source.ReadRule;
    // etc
}

IMO นั้นง่ายกว่าและมีประสิทธิภาพมากขึ้น

แม้ว่าคุณจะตัดสินใจที่จะเล่นต่อ แต่ฉันมีคำแนะนำสองสามข้อ:

  • กำจัดด้านนอกif. คุณไม่จำเป็นต้องใช้มันราวกับว่า Count เป็นศูนย์ร่างกายสำหรับลูปจะไม่ดำเนินการ
  • ใช้ขอบเขตบนสุดพิเศษสำหรับลูป - เป็นสำนวนมากกว่าใน C #:

    for (i = 0; i < _lstAcl.Documents.Count; i++)
  • กำจัดนิพจน์ย่อยทั่วไป:

    var target = _lstAcl.Documents[i];
    // Now use target for the rest of the loop body
  • หากเป็นไปได้ให้ใช้foreachแทนforการเริ่มต้นด้วย:

    foreach (var target in _lstAcl.Documents)

39

ใช้FirstOrDefault อันดับแรกจะไม่คืนค่า null - หากไม่พบองค์ประกอบที่ตรงกันมันจะแสดงข้อยกเว้นที่คุณเห็น

_dsACL.Documents.FirstOrDefault(o => o.ID == id);

19
เพียงเพื่อชี้แจงเล็กน้อย - ขั้นแรกสามารถคืนค่า null โดยทั่วไปหากเพรดิเคตของคุณตรงกับค่า null มันไม่สามารถคืนค่า null ได้ที่นี่เช่นเดียวกับการo.IDโยน NullReferenceException ในค่า null
Jon Skeet

11

จากไลบรารี MSDN:

First<TSource>(IEnumerable<TSource>)วิธีการพ่นยกเว้นถ้าแหล่งที่มีองค์ประกอบไม่ หากต้องการส่งคืนค่าเริ่มต้นแทนเมื่อลำดับต้นทางว่างเปล่าให้ใช้FirstOrDefaultเมธอด


0

สำหรับผู้ที่ประสบปัญหานี้ขณะสร้างตัวควบคุมผ่านเมนูบริบทให้เปิด Visual Studio อีกครั้งในฐานะผู้ดูแลระบบจะแก้ไขได้


-4

บางทีการใช้ Where () ก่อน First () สามารถช่วยคุณได้เนื่องจากปัญหาของฉันได้รับการแก้ไขแล้วในกรณีนี้

var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();

3
สิ่งที่ช่วยคุณได้จริงที่นี่คือการใช้. FirstOrDefault () แทน. First () - โดยใช้. Where (o => o.ID == id) .FirstOrDefault () และ. FirstOrDefault (o => o.ID == id ) จะเหมือนกัน
pwdst

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