Nilai pengembalian maksimum jika kueri kosong

175

Saya punya pertanyaan ini:

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

Apa yang akan terjadi maxShoeSizejika perusahaan 8 tidak memiliki pekerja sama sekali?

UPDATE:
Bagaimana saya bisa mengubah kueri untuk mendapatkan 0 dan bukan pengecualian?

Naor
sumber
Naor: pernahkah Anda mendengar tentang LINQPad?
Mitch Wheat
3
Saya tidak mengerti mengapa Anda bertanya, "Apa yang akan terjadi maxShoeSize?" jika Anda sudah mencobanya.
jwg
@ jwg: Saya rasa saya ingin melihat apakah Anda tahu jawabannya :) Akhirnya saya mendapatkan cara yang lebih baik untuk melakukan apa yang saya minta dan inilah yang saya maksud.
Naor
@Naor, ini bukan game tebak-tebakan. Saya juga akan menurunkan pertanyaan awal. Jika Anda tahu jawabannya berikan kepada kami kalau tidak, Anda terlihat malas. Baru saja saya akan melakukan pertanyaan yang sama dan saya menyiapkan semua info termasuk pesan pengecualian.
Juan Carlos Oropeza

Jawaban:

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

Titik nol DefaultIfEmptytidak perlu.

Ron K.
sumber
Bekerja :) Tetapi pada kode saya nol di DefaultIfEmpty diperlukan.
Carlos Tenorio Pérez
1
Mengenai bagian pertama dari pertanyaan, yang tidak sepenuhnya dijawab di sini: Menurut dokumentasi resmi , jika tipe generik dari urutan kosong adalah tipe referensi, Anda mendapatkan nol. Kalau tidak, menurut pertanyaan ini , memanggil Max()urutan kosong menghasilkan kesalahan.
Raimund Krämer
59

Saya tahu ini adalah pertanyaan lama dan jawaban yang diterima berfungsi, tetapi pertanyaan ini menjawab pertanyaan saya tentang apakah set kosong seperti itu akan menghasilkan pengecualian atau default(int)hasil.

Namun jawaban yang diterima, meskipun berhasil, bukanlah solusi IMHO yang ideal, yang tidak diberikan di sini. Jadi saya memberikannya dalam jawaban saya sendiri untuk kepentingan siapa saja yang mencarinya.

Kode asli OP adalah:

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

Ini adalah bagaimana saya akan menulisnya untuk mencegah pengecualian dan memberikan hasil default:

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

Ini menyebabkan tipe Maxfungsi yang dikembalikan int?, yang memungkinkan nullhasilnya dan kemudian ??menggantikan nullhasilnya dengan 0.


EDIT
Hanya untuk memperjelas sesuatu dari komentar, Entity Framework saat ini tidak mendukung askata kunci, jadi cara untuk menuliskannya ketika bekerja dengan EF adalah:

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

Karena itu [TypeOfWorkers]bisa menjadi nama kelas yang panjang dan membosankan untuk ditulis, saya telah menambahkan metode ekstensi untuk membantu.

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

Ini hanya menangani int, tapi sama bisa dilakukan untuk long, double, atau jenis nilai lain yang Anda perlu. Menggunakan metode ekstensi ini sangat sederhana, Anda hanya meneruskan fungsi pemilih Anda dan secara opsional memasukkan nilai yang akan digunakan untuk null, yang defaultnya ke 0. Jadi di atas bisa ditulis ulang seperti:

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

Semoga itu membantu orang lebih banyak.

CptRobby
sumber
3
Solusi bagus! Jawaban yang lebih populer DefaultIfEmptyhanya berfungsi dengan baik ketika Maxtidak melakukan evaluasi.
McGuireV10
@ McGuireV10 Ya, saya biasanya tidak suka menggunakan Selectsebagai perantara ketika saya hanya akan menggunakan fungsi agregat seperti Maxpada hasilnya. Saya juga berpikir (saya belum menguji ini) bahwa SQL yang dihasilkan akan menggunakan query subselect tambahan dengan melakukan itu, sementara saya hanya akan berurusan dengan set kosong dengan mengembalikan nol. Terima kasih atas upvote dan umpan baliknya! ;)
CptRobby
@ McGuireV10 Demikian pula, jika ShoeSizeitu benar-benar dalam terkait Uniformentitas, saya tidak akan menggunakan Workers.Where(x => x.CompanyId == 8).Select(x => x.Uniform).Max(x => x.ShoeSize), bukannya saya hanya akan menjaga seluruh evaluasi dalam Maxfungsi: Workers.Where(x => x.CompanyId == 8).Max(x => x.Uniform.ShoeSize). Saya lebih suka menggunakan metode sesedikit mungkin dalam pertanyaan saya untuk memungkinkan EF memiliki kebebasan terbesar dalam memutuskan bagaimana membangun pertanyaan secara efisien. ;-)
CptRobby
1
Saya tidak bisa mengaktifkan ini di EntityFramework. Adakah yang bisa menjelaskan? (Menggunakan teknik DefaultIfEmpty berhasil).
Moe Sisko
1
Sebuah versi generik dari metode ekstensi: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;
relatively_random
32

Maks () tidak akan mengembalikan apa pun dalam hal ini.

Ini akan meningkatkan InvalidOperationException karena sumber tidak mengandung elemen.

Frédéric Hamidi
sumber
Ini hanya akan meningkat InvalidOperationExceptionjika objek dalam daftar adalah tipe yang tidak dapat dibatalkan
Dominus.Vobiscum
17
int maxShoeSize = Workers.Where(x => x.CompanyId == 8)
                     .Select(x => x.ShoeSize)
                     .DefaultIfEmpty()
                     .Max();
Cheng Chen
sumber
10

Jika ini Linq to SQL, saya tidak suka menggunakan Any()karena menghasilkan beberapa permintaan ke SQL server.

Jika ShoeSizebukan bidang yang dapat dibatalkan, maka hanya menggunakan .Max(..) ?? 0tidak akan berfungsi tetapi akan berikut:

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

Ini benar-benar tidak mengubah SQL yang dipancarkan, tetapi mengembalikan 0 jika urutannya kosong karena mengubah Max()untuk mengembalikan int?bukan int.

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

(dengan asumsi itu ShoeSizeadalah tipe int)

Jika Workersa DbSetatau ObjectSetdari Entity Framework kueri awal Anda akan melempar InvalidOperationException, tetapi tidak mengeluh tentang urutan kosong tetapi mengeluh bahwa nilai yang terwujud NULL tidak dapat dikonversi menjadi int.

Slauma
sumber
2

Max akan melempar System.InvalidOperationException "Urutan tidak mengandung elemen"

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;
}
Johan Tidén
sumber
2

NB: permintaan dengan DefaultIfEmpty()mungkin jauh lebih lambat . Dalam kasus saya itu adalah kueri sederhana dengan .DefaultIfEmpty(DateTime.Now.Date).

Saya terlalu malas untuk menggambarkannya, tetapi jelas EF mencoba untuk mendapatkan semua baris dan kemudian mengambil Max()nilainya.

Kesimpulan: terkadang penanganan InvalidOperationExceptionmungkin merupakan pilihan yang lebih baik.

Andrey St
sumber
0

Anda dapat menggunakan terner di dalam .Max()untuk menangani predikat dan menetapkan nilainya;

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

Anda perlu menangani Workerskoleksi menjadi nol / kosong jika itu suatu kemungkinan, tetapi itu tergantung pada implementasi Anda.

Jecom
sumber
0

Anda dapat mencoba ini:

int maxShoeSize = Workers.Where(x=>x.CompanyId == 8).Max(x => x.ShoeSize) ?? 0;
Carlos Toledo
sumber
Saya pikir ini akan gagal karena Max mengharapkan int dan mendapatkan null; jadi kesalahan telah terjadi sebelum operator null-coalescing diberlakukan.
d219
0

Anda dapat memeriksa apakah ada pekerja sebelum melakukan 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);
}
Pendeta Sfinks
sumber