Kaynak koleksiyonu boşken LINQ Sum () 0 döndürmeye zorlama


183

Temelde aşağıdaki sorguyu yaptığımda, hiçbir potansiyel müşteri eşleşmediğinde aşağıdaki sorgu bir istisna atar. Bu durumda toplamın atılan bir istisna yerine 0'a eşitlenmesini tercih ederim. Bu sorgunun kendisinde mümkün olabilir mi - yani sorguyu saklamak ve kontrol etmek yerine query.Any()?

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                && l.Date.Month == date.Month
                && l.Date.Year == date.Year
                && l.Property.Type == ProtectedPropertyType.Password
                && l.Property.PropertyId == PropertyId).Sum(l => l.Amount);

2
WhereDönen olmaz nullo herhangi bir kayıt bulamadık, eğer sıfır öğelerin bir listesini döndürecektir. İstisna nedir?
Mike Perrenoud

3
İstisna nedir?
Toto

3
İstisna olsun: 'Int32' değer türüne döküm, gerçekleşen değer boş olduğu için başarısız oldu. Sonuç türünün genel parametresi veya sorgu null olabilecek bir tür kullanmalıdır.
John Mayer

1
@Stijn, hayır, yaptığın şey hala işe yaramazdı. Sorun SQLüretildiği yol . Amountaslında null, sıfır sonucu nasıl ele aldığını çevreleyen bir sorun. Verilen cevaba bir göz atın.
Mike Perrenoud

39
Dolar miktarları için iki katı kullanmamalısınız ! Kesirli dolar miktarları bile. Kesin bir miktar amaçlandığında asla asla iki kat kullanmayın. Veritabanı sütunlarınız olmalı decimal, kodunuzu kullanmalısınız decimal. Programlama kariyerinizde, birisinin size bunları kullanmasını söylediği güne kadar, istatistik veya yıldız parlaklığı veya stokastik bir işlemin veya bir elektronun yükünün sonuçları için olduğunu bildiğinizi floatve unutun double! O zamana kadar, yanlış yapıyorsun .
ErikE

Yanıtlar:


391

Sorgunuzu şu şekilde değiştirmeyi deneyin:

db.Leads.Where(l => l.Date.Day == date.Day
            && l.Date.Month == date.Month
            && l.Date.Year == date.Year
            && l.Property.Type == ProtectedPropertyType.Password
            && l.Property.PropertyId == PropertyId)
         .Select(l => l.Amount)
         .DefaultIfEmpty(0)
         .Sum();

Bu şekilde, sorgunuz yalnızca Amountalanı seçer . Koleksiyon boşsa, değerine sahip bir öğe döndürür 0ve sonra toplam uygulanır.


Kesinlikle hile yapar, ancak Sumveritabanı tarafında yerine önce Amount değerlerinin bir listesini ve bunları sunucu tarafında seçmez mi? imo 2kay'ın çözümü daha optimal, en azından anlamsal olarak daha doğrudur.
Maksim Vi.

3
Zaman @MaksimVI EF, İlk materyalizasyonda üzerine sorgu üretecektir IQueryable<T>zincir durur (genellikle Aradığınızda ToList, AsEnumerablevb .. ve bu durumda Sum). SumEF Queryable Sağlayıcısı tarafından bilinen ve işlenen bir yöntemdir ve ilgili SQL deyimini oluşturur.
Simon Belanger

@SimonBelanger Düzeltildiğimde, tutar DB tarafında yapılır, ancak önce Miktarları seçen bir alt sorguda yapılır. Temel olarak sorgu sadece SELECT SUM(a.Amount) FROM (SELECT Amount FROM Leads WHERE ...) AS ayerine SELECT SUM(Amount) FROM Leads. Ayrıca, alt sorgu ek bir boş kontrol ve tek sıra tablo ile garip bir dış birleşim vardır.
Maksim Vi.

Önemli bir performans farkı değil ve muhtemelen optimize edildi, ancak hala diğer çözümün daha temiz göründüğünü düşünüyorum.
Maksim Vi.

5
Unutmayın DefaultIfEmptyiçeri atmak gerekir bu yüzden LINQ sağlayıcıların bir dizi desteklemediği bir ToList()veya öncesinde bu uygulanabilir böylece bu durumlarda bunu kullanmaya benzer bir şey Nesneleri için LINQ senaryosu.
Christopher King

188

Başka bir kesmek kullanmayı tercih ederim:

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                                      && l.Date.Month == date.Month
                                      && l.Date.Year == date.Year
                                      && l.Property.Type == ProtectedPropertyType.Password
                                      && l.Property.PropertyId == PropertyId)
                          .Sum(l => (double?) l.Amount) ?? 0;

18
Linq to SQL kullanırken, bu kabul edilen yanıttan çok daha kısa bir SQL kodu üretir
wertzui

3
Bu doğru cevap. Diğerleri başarısız. İlk önce boş değerle döküm yapılır ve sonra nihai sonuç boş değerle karşılaştırılır.
Mohsen Afshin

3
Bu Linq To EF için kabul edilen cevaptan çok daha iyi. Benim için oluşturulan SQL DefaultIfEmpty yaklaşık 3.8 kat daha iyi performans.
Florian

2
Bu ÇOK DAHA HIZLI.
frakon

1
Bu kesin kullanım nullables, yani hiçbir veri ve varsayılan değer arasındaki farkı gösteren olarak, bu bir kesmek
demez olmaz


4

Bu benim için kazan:

int Total = 0;
Total = (int)Db.Logins.Where(L => L.id == item.MyId).Sum(L => (int?)L.NumberOfLogins ?? 0);

GİRİŞ tablomda, NUMBEROFLOGINS alanında bazı değerler NULL ve diğerlerinde INT numarası var. Burada, bir Corporation'ın (Her Kimlik) tüm kullanıcılarının toplam NUMBEROFLOGINS değerini toplamıyorum.


1

Deneyin:

çifte kazanç = db.Leads.Where (l => l.ShouldBeIncluded). Toplam (l => (çift?) l.Ayrı) ?? 0 ;

" SELECT SUM ([Amount]) " sorgusu boş liste için NULL döndürür. Ancak LINQ kullanırsanız, " Sum (l => l.Amount) " ifadesinin iki kez döndüğünü ve boş toplama için 0 ayarlamak üzere " ?? " operatörünü kullanmanıza izin vermediğini bekler .

Bu durumu önlemek için LINQ " çift? " Beklemeniz gerekir . Bunu " (double?) L.Amount " dökümünü kullanarak yapabilirsiniz .

SQL sorgusu etkilemez, ancak LINQ boş koleksiyonlar için çalışmasını sağlar.


0
db.Leads.Where(l => l.Date.Day == date.Day
        && l.Date.Month == date.Month
        && l.Date.Year == date.Year
        && l.Property.Type == ProtectedPropertyType.Password
        && l.Property.PropertyId == PropertyId)
     .Select(l => l.Amount)
     .ToList()
     .Sum();

1
Lütfen kodla ilgili bazı bilgiler ekleyin
Jaqen H'ghar

1
Hiçbir şey döndürmediğinden, ToList () olmadan denediğimde hata aldım. Ancak ToList () boş listeyi yapar ve ToList (). Sum () yaptığımda hata vermez.
Mona

2
İstediğiniz ToListtek şey toplam ise, burada kullanmak istemezsiniz. Bu, sonuç kümesinin tamamını (yalnızca Amountbu durumda her kayıt için) belleğe ve daha sonra Sum()bu kümeye döndürür . SQL Server üzerinden hesaplama yapan başka bir çözüm kullanmak çok daha iyi.
Josh M.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.