เลือกหลายระเบียนตามรายการของ Id กับ linq


122

ฉันมีรายการที่มี Id ของUserProfileตารางของฉัน ฉันจะเลือกทั้งหมดUserProfilesตามรายการรหัสที่ฉันvarใช้งานได้LINQอย่างไร?

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);

ฉันติดอยู่ตรงนี้ ฉันสามารถทำได้โดยใช้สำหรับลูปเป็นต้น แต่ฉันควรทำเช่นนี้กับLINQ.


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

5
การถามคำถามเสียเวลามากกว่าการค้นหา ครั้งต่อไปเพียงสมมติว่า 'เขา / เธอ' ทำการค้นหาหรือ 10.
Yustme

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

คำตอบ:


206

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

var userProfiles = _dataContext.UserProfile
                               .Where(t => idList.Contains(t.Id));

ฉันยังสมมติว่าแต่ละUserProfileระเบียนจะมีint Idฟิลด์ หากไม่เป็นเช่นนั้นคุณจะต้องปรับเปลี่ยนตามนั้น


สวัสดีใช่บันทึก userprofile มี id ดังนั้นฉันจะทำบางสิ่งเช่น t => t.id == idList.Contains (id)?
Yustme

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

ปัญหาก็คือ t ถือวัตถุทั้งหมดของ UserProfile และ idList มีเฉพาะ int เท่านั้น คอมไพเลอร์บ่นเกี่ยวกับบางสิ่งบางอย่าง แต่ฉันสามารถแก้ไขได้แล้ว ขอบคุณ
Yustme

2
@Yuck - ใช้งานไม่ได้สำหรับฉันฟังก์ชันหมดเวลาแล้ว! ได้ปิดใช้งานการโหลด Lazy แต่ยังคงล้มเหลว
bhuvin

1
ฉันได้รับ "ไม่สามารถแปลงนิพจน์แลมบ์ดาเป็นพิมพ์" int "ได้เนื่องจากไม่ใช่ประเภทผู้รับมอบสิทธิ์" จะแก้ไขได้อย่างไร?
Stian

92

การแก้ปัญหาด้วย. Where และ .Contains มีความซับซ้อนของ O (N square) ง่าย ๆ การเข้าร่วมควรมีประสิทธิภาพที่ดีขึ้นมาก (ใกล้เคียงกับ O (N) เนื่องจากการแฮช) ดังนั้นรหัสที่ถูกต้องคือ:

_dataContext.UserProfile.Join(idList, up => up.ID, id => id, (up, id) => up);

และตอนนี้ผลจากการวัดของฉัน ฉันสร้าง 100000 UserProfiles และ 100,000 ids เข้าร่วมใช้เวลา 32ms และ. ที่ไหนด้วย. เนื้อหาใช้เวลา 2 นาที 19 วินาที! ฉันใช้ IEnumerable สำหรับการทดสอบนี้เพื่อพิสูจน์คำพูดของฉัน หากคุณใช้ List แทน IEnumerable. Where และ .Contains จะเร็วกว่า อย่างไรก็ตามความแตกต่างมีความสำคัญ เร็วที่สุดโดยที่เนื้อหาอยู่กับ Set <> ทั้งหมดขึ้นอยู่กับความซับซ้อนของ coletions พื้นฐานสำหรับ. ดูโพสต์นี้เพื่อเรียนรู้เกี่ยวกับความซับซ้อนของ linq ดูตัวอย่างการทดสอบของฉันด้านล่าง:

    private static void Main(string[] args)
    {
        var userProfiles = GenerateUserProfiles();
        var idList = GenerateIds();
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        userProfiles.Join(idList, up => up.ID, id => id, (up, id) => up).ToArray();
        Console.WriteLine("Elapsed .Join time: {0}", stopWatch.Elapsed);
        stopWatch.Restart();
        userProfiles.Where(up => idList.Contains(up.ID)).ToArray();
        Console.WriteLine("Elapsed .Where .Contains time: {0}", stopWatch.Elapsed);
        Console.ReadLine();
    }

    private static IEnumerable<int> GenerateIds()
    {
       // var result = new List<int>();
        for (int i = 100000; i > 0; i--)
        {
            yield return i;
        }
    }

    private static IEnumerable<UserProfile> GenerateUserProfiles()
    {
        for (int i = 0; i < 100000; i++)
        {
            yield return new UserProfile {ID = i};
        }
    }

เอาต์พุตคอนโซล:

ผ่านไปเวลาเข้าร่วม: 00: 00: 00.0322546

ผ่านไปแล้วที่ไหนประกอบด้วยเวลา: 00: 02: 19.4072107


4
คุณสามารถสำรองข้อมูลด้วยตัวเลขได้หรือไม่?
Yustme

ดี แต่ทำให้ฉันอยากรู้ว่าเวลาจะเป็นอย่างไรเมื่อListใช้ +1
Yustme

ตกลงนี่คือการกำหนดเวลาที่คุณสนใจ: รายการใช้เวลา 13.1 วินาทีและ HashSet ใช้เวลา 0,7 ms! ดังนั้น. Where .Contains จะดีที่สุดในกรณีของ HashSet เท่านั้น (เมื่อ .Contains มีความซับซ้อน O (1)) ในกรณีอื่น ๆ การเข้าร่วมดีกว่า
David Gregor

5
ฉันได้รับLocal sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.ข้อผิดพลาดเมื่อใช้ LINQ2SQL datacontext
Mayank Raichura

3
@Yustme - ประสิทธิภาพการทำงานมักจะพิจารณา (ฉันเกลียดที่จะเป็นผู้ชาย "นี่ควรเป็นคำตอบที่ยอมรับ" แต่ ... )
jleach

19

คำตอบที่ดีน่ารัก แต่อย่าลืมสิ่งสำคัญอย่างหนึ่งนั่นคือให้ผลลัพธ์ที่แตกต่างกัน!

  var idList = new int[1, 2, 2, 2, 2]; // same user is selected 4 times
  var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e)).ToList();

สิ่งนี้จะส่งคืน 2 แถวจาก DB (ซึ่งอาจถูกต้องหากคุณต้องการเพียงรายชื่อผู้ใช้ที่แยกจากกัน)

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

  var priceListIDs = new int[1, 2, 2, 2, 2]; // user has bought 4 times item ID 2
  var shoppingCart = _dataContext.ShoppingCart
                     .Join(priceListIDs, sc => sc.PriceListID, pli => pli, (sc, pli) => sc)
                     .ToList();

สิ่งนี้จะแสดงผลลัพธ์5 รายการจาก DB การใช้ "มี" จะไม่ถูกต้องในกรณีนี้


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