Giá trị trả về tối đa nếu truy vấn trống


175

Tôi có truy vấn này:

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

Điều gì sẽ xảy ra maxShoeSizenếu công ty 8 không có công nhân nào cả?

CẬP NHẬT:
Làm cách nào tôi có thể thay đổi truy vấn để nhận 0 và không phải là ngoại lệ?


Naor: bạn đã nghe nói về LINQPad chưa?
Mitch Wheat

3
Tôi không hiểu tại sao bạn lại hỏi 'Cái gì sẽ vào maxShoeSize?' nếu bạn đã thử nó
jwg

@jwg: Tôi đoán tôi muốn xem nếu bạn biết câu trả lời :) Cuối cùng, tôi có một cách tốt hơn để làm những gì tôi yêu cầu và đây là những gì tôi muốn nói.
Naor

@Naor, đây không phải là một trò chơi đoán. Tôi cũng sẽ downvote câu hỏi ban đầu. Nếu bạn biết câu trả lời hãy đưa nó cho chúng tôi nếu không bạn trông lười biếng. Vừa nãy tôi cũng định làm một câu hỏi tương tự và tôi chuẩn bị tất cả thông tin bao gồm cả tin nhắn ngoại lệ.
Juan Carlos Oropeza

Câu trả lời:


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

Số không trong DefaultIfEmptylà không cần thiết.


Hoạt động :) Nhưng trên mã của tôi, số 0 trong Default IfEmpty là cần thiết.
Carlos Tenorio Pérez

1
Về phần đầu tiên của câu hỏi, chưa được trả lời đầy đủ ở đây: Theo tài liệu chính thức , nếu loại chung của chuỗi trống là loại tham chiếu, bạn sẽ nhận được null. Mặt khác, theo câu hỏi này , việc gọi Max()một chuỗi trống dẫn đến một lỗi.
Raimund Krämer

59

Tôi biết đây là một câu hỏi cũ và câu trả lời được chấp nhận hoạt động, nhưng câu hỏi này đã trả lời câu hỏi của tôi về việc một tập hợp trống như vậy sẽ dẫn đến một ngoại lệ hoặc default(int)kết quả.

Tuy nhiên, câu trả lời được chấp nhận, trong khi nó vẫn hoạt động, không phải là giải pháp lý tưởng IMHO, không được đưa ra ở đây. Vì vậy, tôi đang cung cấp nó trong câu trả lời của riêng tôi vì lợi ích của bất cứ ai đang tìm kiếm nó.

Mã ban đầu của OP là:

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

Đây là cách tôi sẽ viết nó để ngăn chặn ngoại lệ và cung cấp kết quả mặc định:

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

Điều này gây ra kiểu trả về của Maxhàm int?, cho phép nullkết quả và sau đó ??thay thế nullkết quả bằng 0.


EDIT
Chỉ cần làm rõ điều gì đó từ các bình luận, Entity Framework hiện không hỗ trợ astừ khóa, vì vậy cách viết từ này khi làm việc với EF sẽ là:

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

[TypeOfWorkers]có thể là một tên lớp dài và rất tẻ nhạt để viết, tôi đã thêm một phương thức mở rộng để trợ giúp.

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

Điều này chỉ xử lý int, nhưng như vậy có thể được thực hiện cho long, doublehoặc bất kỳ loại giá trị khác mà bạn cần. Sử dụng phương thức tiện ích mở rộng này rất đơn giản, bạn chỉ cần truyền vào hàm chọn của mình và tùy ý bao gồm một giá trị được sử dụng cho null, mặc định là 0. Vì vậy, ở trên có thể được viết lại như vậy:

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

Hy vọng rằng sẽ giúp mọi người nhiều hơn nữa.


3
Giải pháp tuyệt vời! DefaultIfEmptyCâu trả lời phổ biến hơn chỉ hoạt động tốt khi Maxkhông thực hiện đánh giá.
McGuireV10

@ McGuireV10 Vâng, tôi thường không thích sử dụng Selectnhư một người trung gian khi tôi sẽ sử dụng một hàm tổng hợp như Maxtrên kết quả. Tôi cũng nghĩ rằng (tôi chưa kiểm tra điều này) rằng SQL được tạo sẽ sử dụng một truy vấn phụ chọn thêm bằng cách thực hiện điều đó, trong khi của tôi sẽ chỉ xử lý một tập hợp trống bằng cách trả về null. Cảm ơn các upvote và thông tin phản hồi! ;)
CptRulk

@ McGuireV10 Tương tự, nếu ShoeSizethực sự là trong một Uniformthực thể có liên quan , tôi sẽ không sử dụng Workers.Where(x => x.CompanyId == 8).Select(x => x.Uniform).Max(x => x.ShoeSize), thay vào đó tôi sẽ chỉ giữ toàn bộ đánh giá trong Maxhàm : Workers.Where(x => x.CompanyId == 8).Max(x => x.Uniform.ShoeSize). Tôi thích sử dụng càng ít phương thức càng tốt trong các truy vấn của mình để cho phép EF có quyền tự do lớn nhất trong việc quyết định cách xây dựng các truy vấn hiệu quả. ;-)
CptRulk

1
Tôi không thể làm điều này để làm việc trong EntityFramework. Co ai đo co thể che đi bong đen nao đo không? (Sử dụng kỹ thuật Default IfEmpty hoạt động).
Moe Sisko

1
Một phiên bản chung của phương thức mở rộng: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;
tương


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

10

Nếu đây là Linq to SQL, tôi không muốn sử dụng Any()vì nó dẫn đến nhiều truy vấn đến máy chủ SQL.

Nếu ShoeSizekhông phải là trường rỗng, thì chỉ sử dụng .Max(..) ?? 0sẽ không hoạt động nhưng sau đây sẽ:

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

Nó hoàn toàn không thay đổi SQL được phát ra, nhưng nó trả về 0 nếu chuỗi trống vì nó thay đổi Max()để trả về một int?thay vì một int.


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

(giả sử đó ShoeSizelà loại int)

Nếu WorkersDbSethoặc ObjectSettừ Entity Framework, truy vấn ban đầu của bạn sẽ đưa ra InvalidOperationException, nhưng không phàn nàn về một chuỗi trống mà phàn nàn rằng giá trị cụ thể hóa NULL không thể được chuyển đổi thành một int.


2

Max sẽ ném System.InvalidOperationException "Chuỗi không chứa phần tử"

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

NB: truy vấn với DefaultIfEmpty()có thể chậm hơn đáng kể . Trong trường hợp của tôi đó là một truy vấn đơn giản với.DefaultIfEmpty(DateTime.Now.Date) .

Tôi đã quá lười biếng để lập hồ sơ nhưng rõ ràng là EF đã cố gắng để có được tất cả các hàng và sau đó lấy Max() giá trị.

Kết luận: đôi khi xử lý InvalidOperationExceptioncó thể là sự lựa chọn tốt hơn.


0

Bạn có thể sử dụng một ternary bên trong .Max()để xử lý vị ngữ và đặt giá trị của nó;

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

Bạn sẽ cần xử lý Workersbộ sưu tập là null / trống nếu đó là một khả năng, nhưng nó sẽ phụ thuộc vào việc triển khai của bạn.


0

Bạn có thể thử điều này:

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

Tôi nghĩ điều này sẽ thất bại vì Max đang mong đợi một int và nó đang trở nên vô giá trị; do đó, một lỗi đã xảy ra trước khi toán tử hợp nhất null có hiệu lực.
d219

0

Bạn có thể kiểm tra nếu có bất kỳ công nhân nào trước khi thực hiện 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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.