สร้าง Tuple ใน Linq Select


89

ฉันกำลังทำงานกับ C # และ. NET Framework 4.5.1 เพื่อดึงข้อมูลจากฐานข้อมูล SQL Server ด้วย Entity Framework 6.1.3

ฉันมีสิ่งนี้:

codes = codesRepo.SearchFor(predicate)
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

และเมื่อเรียกใช้ฉันจะได้รับข้อความนี้:

รองรับเฉพาะตัวสร้างและตัวเริ่มต้นที่ไม่มีพารามิเตอร์เท่านั้นใน LINQ ถึงเอนทิตี

ฉันไม่รู้ว่าฉันต้องสร้าง Tuple อย่างไรเพราะตัวอย่างทั้งหมดที่ฉันพบส่วนใหญ่เป็นแบบนี้

ฉันได้ลองสิ่งนี้:

codes = codesRepo.SearchFor(predicate)
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

และรับข้อผิดพลาดนี้:

LINQ เป็นเอนทิตีไม่รู้จักเมธอด 'System.Tuple'2 [System.String, System Byte] Create [String, Byte] (System.String, Byte)' และวิธีนี้ไม่สามารถแปลเป็นนิพจน์ที่เก็บได้

ปัญหาอยู่ที่ไหน


ดูเหมือนว่าคุณจะต้องสร้างวัตถุที่พิมพ์ผิด แต่ใช่คำถามที่ดี โหวตแล้ว
เฟรนลี่

คำตอบ:


121

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

codes = codesRepo.SearchFor(predicate)
    .Select(c => new { c.Id, c.Flag })
    .AsEnumerable()
    .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
    .ToList();

หมายเหตุ:กฎข้างต้นใช้กับ EF6 EF Core รองรับ tuples อย่างเป็นธรรมชาติ (ในการฉายภาพหรือเป็นคีย์เข้าร่วม / กลุ่ม) ผ่านตัวสร้างทูเปิลเช่นแบบสอบถามดั้งเดิมใช้งานได้ง่าย

codes = codesRepo.SearchFor(predicate)
  .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
  .ToList();

แต่ไม่ใช่Tuple.Createวิธีการ (EF Core 2.x)


ทางออกที่ดีมาก - ขอบคุณ! ... แล้วถ้าฉันต้องขยายโซลูชันนี้ด้วยค่าว่างอื่นล่ะ? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })ไม่ทำงาน, ไม่เป็นผล.
skyfrog

2
@skyfrog ตัวดำเนินการ?.ไม่ได้รับการสนับสนุนในแผนภูมินิพจน์ แต่นอกเหนือจากนั้นคุณสามารถขยายประเภทที่ไม่ระบุตัวตนด้วยค่าได้มากเท่าที่คุณต้องการ - อย่าลืมตั้งชื่อเมื่อจำเป็น :) เช่นc => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev

1
เยี่ยมมาก! ขอบคุณมาก @Ivan สำหรับการตอบกลับของคุณ ฉันสนิทมาก! ... แต่มันก็พูดง่ายเสมอในขณะที่มองย้อนกลับไป ;-)
skyfrog

คำตอบที่ดีใช้ได้กับ EF-Entities .. เช่น dbCtx.MyEntity.Where () เลือก (.. ถึง anon object ... ) ฯลฯ ...
joedotnot

50

เป็นเพียงคำตอบที่อัปเดตสำหรับ C # 7 ตอนนี้คุณสามารถใช้ไวยากรณ์ที่ง่ายขึ้นเพื่อสร้าง ValueTuples

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();

คุณสามารถตั้งชื่อคุณสมบัติของทูเปิลได้ทันที:

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

ดังนั้นแทนที่จะใช้เป็น Item1 หรือ Item2 คุณสามารถเข้าถึงได้ในรูปแบบ Id หรือ Flag

เอกสารเพิ่มเติมเกี่ยวกับการเลือกระหว่างแบบไม่ระบุตัวตนและทูเพิล


11

ลองสิ่งนี้:

codes = codesRepo.SearchFor(predicate)
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

ได้รับแจ้งว่าไม่ยอมรับใน LINQ กับเอนทิตี

อีกทางเลือกหนึ่งคือดึงผลลัพธ์ลงในหน่วยความจำก่อนที่จะเลือก หากคุณกำลังจะทำสิ่งนี้ฉันขอแนะนำให้ทำการกรองทั้งหมดก่อน. AsEnumerable () เนื่องจากหมายความว่าคุณดึงเฉพาะผลลัพธ์ที่คุณต้องการกลับมาแทนที่จะดึงตารางทั้งหมดกลับมาแล้วจึงกรอง

codes = codesRepo.SearchFor(predicate).AsEnumerable()
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

เช่นกัน Tuple.Create (c.Id, c.Flag) สามารถเปลี่ยนเป็น Tuple (c.Id, c.Flag) ใหม่ได้หากคุณต้องการทำให้โค้ดมีความชัดเจนมากขึ้นในประเภท tuples


ขออภัยไม่สามารถใช้งานได้ ฉันได้อัปเดตคำถามของฉันพร้อมรายละเอียดเพิ่มเติม
VansFannel

10

ในlinq ไปยังเอนทิตีคุณสามารถฉายลงในประเภทที่ไม่ระบุตัวตนหรือลงใน DTO เพื่อหลีกเลี่ยงปัญหานั้นคุณสามารถใช้AsEnumerableวิธีการขยาย:

codes = codesRepo.SearchFor(predicate).AsEnumerable().
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

วิธีนี้ช่วยให้คุณสามารถทำงานกับLinq to ObjectแทนLinq to Entitiesได้ดังนั้นหลังจากเรียกใช้แล้วคุณสามารถแสดงผลลัพธ์ของแบบสอบถามของคุณในสิ่งที่คุณต้องการข้อดีของการใช้AsEnumerableแทนToListคือAsEnumerableไม่ดำเนินการสืบค้น แต่จะรักษาการดำเนินการที่รอการตัดบัญชี เป็นความคิดที่ดีเสมอกรองข้อมูลของคุณก่อนเรียกใช้วิธีใดวิธีหนึ่งเหล่านี้


-1 สิ่งนี้ไม่ได้ทำสิ่งเดียวกับที่ OP ถาม บางครั้งความสามารถในการสร้างทูเปิลในแบบสอบถามเพื่อจุดประสงค์ในการเข้าร่วมเป็นสิ่งสำคัญ
Aron


1

ใช้วิธีนี้เพื่อทำสิ่งนี้และใช้ async

var codes = await codesRepo.SearchFor(predicate)
                    .Select(s => new
                    {
                        Id = s.Id,
                        Flag = s.Flag
                    }).FirstOrDefaultAsync();

                var return_Value = new Tuple<string, byte>(codes.Id, codes.Flag);

0

แค่สองเซ็นต์ของฉัน: สิ่งนี้ทำให้ฉันรู้สึกไม่กี่ครั้งด้วยชื่อประเภท:

ตัวอย่างเล็ก ๆ น้อย ๆ :

    private Tuple<string, byte> v1()
    {
        return new Tuple<string, byte>("", 1);
    }

    private (string, int) v2()
    {
        return ("", 1);
    }

    private (string Id, byte Flag) v3()
    {
        return ("", 1);
    }

ความนับถือ.


ไวยากรณ์ที่คุณโพสต์ไม่ทำงาน สิ่งที่คุณอาจหมายถึงการเขียนคือpublic (string Id, byte Flag) SearchFor(Expression predicate)แต่สิ่งนี้อยู่ข้างประเด็น สองเซ็นต์ไม่ควรเป็นคำตอบ แต่เป็นความคิดเห็น
M.Stramm

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

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