Pertanyaan Mengapa Pernyataan IF memengaruhi hasil Pernyataan LINQ saya?


Saya telah bekerja sedikit dengan LINQ baru-baru ini, dan berkat bantuan beberapa StackOverflowers saya bisa mendapatkan pernyataan ini berfungsi:

var traceJob =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

if (traceJob != null && traceJob.Count() == 1)
{
 traceJob.First().RunNow();
 Console.WriteLine(traceJob.First().DisplayName + "  Last Run Time: " + traceJob.First().LastRunTime);
}

Namun, saya bingung karena bagian yang membuatnya berfungsi adalah if(traceJob.Count() ==1). Jika saya menghapus bagian itu, maka saya mendapatkan ObjectNullRef kesalahan mengatakan bahwa penghitungan traceJob tidak membuahkan hasil.

Sekarang, sepengetahuan saya, sebuah if pernyataan yang memeriksa jumlah seharusnya tidak benar-benar mengubah hasil pernyataan Linq, bukan? Adakah yang bisa menjelaskan kepada saya mengapa saya melihat perilaku ini?


6
2017-11-26 16:50


asal


Jawaban:


Tidak seharusnya. Tebakan saya adalah Anda telah mengalami kasus di mana pencacahan benar-benar kosong, dan dengan memeriksa hitungan> 0, First() tidak gagal.

Sebagai catatan tambahan, Any() mungkin lebih baik cek di sini sebagai (tergantung pada penyimpanan yang mendasari repositori Anda) mungkin lebih cepat daripada Count():

if (traceJob != null && traceJob.Any())
{
 traceJob.First().RunNow();
 Console.WriteLine(traceJob.First().DisplayName + "  Last Run Time: " + traceJob.First().LastRunTime);
}

6
2017-11-26 16:57



var traceJob =
    (from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition).SingleOrDefault();

Anda dapat menggunakan singleOrDefault untuk memeriksa hasil tunggal. Ini akan mengembalikan hasil yang cocok dengan where kondisi atau null jika tidak ditemukan kecocokan. Jika lebih dari satu yang cocok untuk kueri Anda ditemukan pengecualian dibuang.

Ini mencakup Anda tracejob == null sebaik tracejob.count == 1 kondisi.

Artikel MSDN


4
2017-11-26 16:58



Saya tidak tahu implementasi sebenarnya dari "layanan" Anda, tetapi biasanya pertanyaan LINQ benar-benar mengisi hasil mereka hanya ketika diminta. Jadi Hitung () mengubah keadaan traceJob, kemungkinan besar mengumpulkan koleksi internal. Dan sepertinya First () tidak mengisi koleksi internal atau tidak melakukannya dengan benar meskipun biasanya seharusnya.


2
2017-11-26 16:56



Sehubungan dengan https://stackoverflow.com/a/1745716/1289709
Saya pikir menggunakan FirstOrDefault lebih masuk akal di sini.

Setiap kali Anda menggunakan SingleOrDefault, Anda dengan jelas menyatakan bahwa permintaan   harus menghasilkan paling banyak satu hasil. Di sisi lain, kapan   FirstOrDefault digunakan, permintaan dapat mengembalikan jumlah hasil apa pun tetapi   Anda menyatakan bahwa Anda hanya menginginkan yang pertama.

Jadi ubah kode Anda menjadi sesuatu seperti ini:

var traceJob = (from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition).FirstOrDefault();

    if (traceJob != null)
    {
       traceJob.RunNow();
       Console.WriteLine(traceJob.DisplayName + "  Last Run Time: " + traceJob.LastRunTime);
    }

2
2017-11-26 17:04



Saya pikir masalah Anda muncul dari bagaimana pernyataan linq dieksekusi ketika mereka dipanggil, bukan dari tempat mereka dinyatakan.

Jadi, pernyataan Anda:

var traceJob =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

Ini kira-kira setara secara fungsional dengan:

IEnumerable<JobDefinition> GetJobDefinitions(YourService service, Guid traceGuid) 
{
    foreach(var jobDefinition in service.JobDefinitions) 
        if(jobDefinition.Id == traceGuid) 
            yield return jobDefinition;
}

Jadi ketika Anda menelepon traceJob.Count() Anda melakukan hal yang sama dengan menelepon GetJobDefinitions(service, traceGuid).Count() dan setiap kali Anda menelepon traceJob.First() Anda menekan loop lagi.

Ini mungkin bukan masalah jika service.JobDefinitions dapat dipanggil berulang kali. Namun jika hasilnya berubah dari waktu ke waktu (misalnya jika pekerjaan ditambahkan saat Anda mengeksekusi) atau jika proses selanjutnya memiliki hasil yang berbeda, Anda akan mengalami masalah.

Dalam hal apapun Anda mungkin lebih baik menjalankan loop hanya sekali:

var traceJobs =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

// This is where the loop above actually executes - if it's empty it will return null
var firstJob = traceJobs.FirstorDefault();

if(firstJob != null)
{
    firstJob.RunNow();
    Console.WriteLine(firstJob.DisplayName + "  Last Run Time: " + firstJob.LastRunTime);
}

Atau Anda dapat memaksa loop untuk mengeksekusi dengan mengubahnya menjadi daftar atau larik:

var traceJobsExecuted = traceJobs.ToList();

2
2017-11-26 17:09