ค่าส่งคืนสูงสุดหากแบบสอบถามว่างเปล่า


175

ฉันมีคำถามนี้:

int maxShoeSize = Workers
    .Where(x => x.CompanyId == 8)
    .Max(x => x.ShoeSize);

จะเกิดอะไรขึ้นmaxShoeSizeถ้า บริษัท 8 ไม่มีพนักงานเลย?

อัปเดต:
ฉันจะเปลี่ยนคิวรีได้อย่างไรเพื่อให้ได้ 0 และไม่ใช่ข้อยกเว้น


Naor: คุณเคยได้ยิน LINQPad ไหม
มิทช์ข้าวสาลี

3
ฉันไม่เข้าใจว่าทำไมคุณจะถามว่า 'จะเกิดอะไรขึ้นmaxShoeSize?' หากคุณได้ลองแล้ว
jwg

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

@Nor นี่ไม่ใช่เกมที่คาดเดา ฉันจะลงคะแนนคำถามเดิมด้วย ถ้าคุณรู้คำตอบให้เราไม่เช่นนั้นคุณจะดูขี้เกียจ ตอนนี้ฉันกำลังจะทำคำถามเดียวกันและฉันเตรียมข้อมูลทั้งหมดรวมถึงข้อความข้อยกเว้น
Juan Carlos Oropeza

คำตอบ:


298
int maxShoeSize = Workers.Where(x => x.CompanyId == 8)
                         .Select(x => x.ShoeSize)
                         .DefaultIfEmpty(0)
                         .Max();

DefaultIfEmptyไม่จำเป็นต้องเป็นศูนย์


ทำงานได้ :) แต่ในรหัสของฉันศูนย์ใน DefaultIfEmpty จำเป็น
Carlos Tenorio Pérez

1
เกี่ยวกับส่วนแรกของคำถามซึ่งยังไม่ได้รับคำตอบอย่างสมบูรณ์ที่นี่: ตามเอกสารอย่างเป็นทางการหากประเภททั่วไปของลำดับที่ว่างเปล่าเป็นประเภทอ้างอิงคุณจะได้รับ null มิฉะนั้นตามคำถามนี้การโทรMax()ตามลำดับที่ว่างเปล่าส่งผลให้เกิดข้อผิดพลาด
Raimund Krämer

59

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

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

รหัสดั้งเดิมของ OP คือ:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize);

นี่คือวิธีที่ฉันจะเขียนเพื่อป้องกันข้อยกเว้นและให้ผลลัพธ์เริ่มต้น:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize as int?) ?? 0;

นี่เป็นสาเหตุชนิดกลับของMaxฟังก์ชั่นที่จะเป็นint?ซึ่งจะช่วยให้nullผลแล้ว??แทนที่ผลกับ null0


แก้ไข
เพื่อชี้แจงบางสิ่งจากความคิดเห็น Entity Framework ไม่สนับสนุนasคำหลักในปัจจุบันดังนั้นวิธีเขียนเมื่อทำงานกับ EF จะเป็น:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max<[TypeOfWorkers], int?>(x => x.ShoeSize) ?? 0;

เนื่องจาก[TypeOfWorkers]อาจเป็นชื่อชั้นยาวและน่าเบื่อในการเขียนฉันจึงได้เพิ่มวิธีการขยายเพื่อช่วยออก

public static int MaxOrDefault<T>(this IQueryable<T> source, Expression<Func<T, int?>> selector, int nullValue = 0)
{
    return source.Max(selector) ?? nullValue;
}

นี้จะจับintแต่เดียวกันสามารถทำได้สำหรับlong, doubleหรือประเภทค่าอื่น ๆ ที่คุณจำเป็นต้อง การใช้วิธีการขยายนี้เป็นเรื่องง่ายมากคุณเพียงแค่ส่งผ่านฟังก์ชั่นตัวเลือกของคุณและเลือกที่จะรวมค่าที่จะใช้เป็นค่าว่างซึ่งเริ่มต้นที่ 0 ดังนั้นด้านบนสามารถเขียนใหม่ได้เช่น:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).MaxOrDefault(x => x.ShoeSize);

หวังว่าจะช่วยให้ผู้คนออกไปมากยิ่งขึ้น


3
สุดยอดทางออก! DefaultIfEmptyคำตอบที่นิยมมากขึ้นใช้ได้ดีเมื่อMaxไม่ได้ทำการประเมินเท่านั้น
McGuireV10

@ McGuireV10 ใช่ฉันไม่ชอบใช้Selectเป็นคนกลางเมื่อฉันเพิ่งจะใช้ฟังก์ชั่นรวมเหมือนMaxในผลลัพธ์ ฉันยังคิดว่า (ฉันยังไม่ได้ทดสอบสิ่งนี้) ว่า SQL ที่สร้างขึ้นจะใช้แบบสอบถามย่อยเลือกพิเศษโดยทำเช่นนั้นในขณะที่ Mine จะจัดการกับชุดว่างโดยส่งคืน null ขอบคุณสำหรับ upvote และข้อเสนอแนะ! ;)
CptRobby

@ McGuireV10 ในทำนองเดียวกันถ้าShoeSizeเป็นจริงในที่เกี่ยวข้องUniformนิติบุคคลฉันจะไม่ใช้Workers.Where(x => x.CompanyId == 8).Select(x => x.Uniform).Max(x => x.ShoeSize)แทนฉันเพียงแค่จะทำให้การประเมินผลทั้งในฟังก์ชั่น:Max Workers.Where(x => x.CompanyId == 8).Max(x => x.Uniform.ShoeSize)ฉันต้องการใช้วิธีการที่น้อยที่สุดเท่าที่จะเป็นไปได้ในแบบสอบถามของฉันเพื่อให้ EF มีอิสระมากที่สุดในการตัดสินใจว่าจะสร้างแบบสอบถามได้อย่างมีประสิทธิภาพ ;-)
CptRobby

1
ฉันไม่สามารถใช้มันใน EntityFramework ทุกคนสามารถหลั่งน้ำตาแสงใด ๆ ? (การใช้เทคนิค DefaultIfEmpty ทำงาน)
Moe Sisko

1
รุ่นทั่วไปของวิธีการขยาย:public static TResult MaxOrDefault<TElement, TResult>(this IQueryable<TElement> items, Expression<Func<TElement, TResult>> selector, TResult defaultValue = default) where TResult : struct => items.Select(selector).Max(item => (TResult?)item) ?? defaultValue;
ค่อนข้าง

32

Max ()จะไม่ส่งคืนสิ่งใดในกรณีนั้น

มันจะยกInvalidOperationExceptionเนื่องจากแหล่งที่มาไม่มีองค์ประกอบ


มันจะเพิ่มขึ้นเฉพาะในInvalidOperationExceptionกรณีที่วัตถุในรายการเป็นประเภทที่ไม่เป็นโมฆะ
Dominus.Vobiscum


10

ถ้านี่คือ Linq ไปยัง SQL ฉันไม่ชอบที่จะใช้Any()เพราะมันส่งผลให้หลายแบบสอบถามไปยังเซิร์ฟเวอร์ SQL

ถ้าShoeSizeไม่ใช่เขตข้อมูล null ให้ใช้เพียง.Max(..) ?? 0จะไม่ทำงาน แต่จะต่อไปนี้:

int maxShoeSize = Workers.Where(x = >x.CompanyId == 8).Max(x => (int?)x.ShoeSize) ?? 0;

มันอย่างไม่เปลี่ยนแปลง SQL ที่ปล่อยออกมา แต่มันก็ไม่กลับมา 0 ถ้าลำดับที่ว่างเปล่าเพราะมันเปลี่ยนแปลงMax()ไปกลับแทนint?int


4
int maxShoeSize=Workers.Where(x=>x.CompanyId==8)
    .Max(x=>(int?)x.ShoeSize).GetValueOrDefault();

(สมมติว่าShoeSizeเป็นประเภทint)

หากWorkersเป็นDbSetหรือObjectSetจาก Entity Framework แบบสอบถามเริ่มต้นของคุณจะโยนInvalidOperationExceptionแต่ไม่บ่นเกี่ยวกับลำดับที่ว่างเปล่า แต่บ่นว่า materialized ค่า NULL intไม่สามารถแปลงเป็น


2

Max จะโยน System.InvalidOperationException "Sequence ไม่มีองค์ประกอบ"

class Program
{
    static void Main(string[] args)
    {
        List<MyClass> list = new List<MyClass>();

        list.Add(new MyClass() { Value = 2 });

        IEnumerable<MyClass> iterator = list.Where(x => x.Value == 3); // empty iterator.

        int max = iterator.Max(x => x.Value); // throws System.InvalidOperationException
    }
}

class MyClass
{
    public int Value;
}

2

หมายเหตุ: แบบสอบถามที่มีDefaultIfEmpty()อาจจะมีนัยสำคัญช้าลง .DefaultIfEmpty(DateTime.Now.Date)ในกรณีของฉันที่เป็นแบบสอบถามที่เรียบง่ายด้วย

ฉันขี้เกียจเกินกว่าจะโพรไฟล์ได้ แต่เห็นได้ชัดว่า EF พยายามที่จะรับทุกแถวจากนั้นรับMax()ค่า

สรุป: บางครั้งการจัดการInvalidOperationExceptionอาจเป็นตัวเลือกที่ดีกว่า


0

คุณสามารถใช้ ternary ภายใน.Max()เพื่อจัดการเพรดิเคตและตั้งค่า

// assumes Workers != null && Workers.Count() > 0
int maxShoeSize = Workers.Max(x => (x.CompanyId == 8) ? x.ShoeSize : 0);

คุณจะต้องจัดการกับWorkersคอลเลกชันที่เป็นโมฆะ / ว่างเปล่าถ้าเป็นไปได้ แต่มันจะขึ้นอยู่กับการใช้งานของคุณ


0

คุณสามารถลองสิ่งนี้:

int maxShoeSize = Workers.Where(x=>x.CompanyId == 8).Max(x => x.ShoeSize) ?? 0;

ฉันคิดว่าสิ่งนี้จะล้มเหลวเนื่องจาก Max คาดว่าจะเป็น int และจะได้รับเป็นโมฆะ ดังนั้นข้อผิดพลาดได้เกิดขึ้นแล้วก่อนที่ผู้ประกอบการ null รวมกันมีผลบังคับใช้
d219

0

คุณสามารถตรวจสอบว่ามีพนักงานคนใดก่อนที่จะทำ Max ()

private int FindMaxShoeSize(IList<MyClass> workers) {
   var workersInCompany = workers.Where(x => x.CompanyId == 8);
   if(!workersInCompany.Any()) { return 0; }
   return workersInCompany.Max(x => x.ShoeSize);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.