Maximaler Rückgabewert bei leerer Abfrage


175

Ich habe diese Frage:

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

Was wird passieren, maxShoeSizewenn Unternehmen 8 überhaupt keine Mitarbeiter hat?

UPDATE:
Wie kann ich die Abfrage ändern, um 0 und keine Ausnahme zu erhalten?


Naor: Hast du von LINQPad gehört?
Mitch Wheat

3
Ich verstehe nicht, warum Sie fragen würden: "Was wird drin sein maxShoeSize?" wenn du es schon ausprobiert hättest.
JWG

@jwg: Ich denke, ich wollte sehen, ob du die Antwort kennst :) Irgendwann habe ich einen besseren Weg gefunden, um das zu tun, was ich gefragt habe, und das habe ich gemeint.
Naor

@Naor, das ist kein Ratespiel. Ich würde auch die ursprüngliche Frage ablehnen. Wenn Sie die Antwort kennen, geben Sie sie uns, sonst sehen Sie faul aus. Gerade wollte ich die gleiche Frage stellen und bereite alle Informationen einschließlich der Ausnahmemeldung vor.
Juan Carlos Oropeza

Antworten:


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

Die Nullstellung DefaultIfEmptyist nicht erforderlich.


Funktioniert :) Aber in meinem Code war die Null in DefaultIfEmpty notwendig.
Carlos Tenorio Pérez

1
Zum ersten Teil der Frage, der hier nicht vollständig beantwortet wird: Laut der offiziellen Dokumentation erhalten Sie null , wenn der generische Typ der leeren Sequenz ein Referenztyp ist. Andernfalls führt gemäß dieser Frage das Aufrufen Max()einer leeren Sequenz zu einem Fehler.
Raimund Krämer

59

Ich weiß, dass dies eine alte Frage ist und die akzeptierte Antwort funktioniert, aber diese Frage beantwortete meine Frage, ob eine solche leere Menge zu einer Ausnahme oder einem default(int)Ergebnis führen würde.

Die akzeptierte Antwort ist jedoch, obwohl sie funktioniert, meiner Meinung nach nicht die ideale Lösung, die hier nicht angegeben wird. Daher gebe ich es in meiner eigenen Antwort zum Nutzen aller, die danach suchen.

Der ursprüngliche Code des OP war:

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

So würde ich es schreiben, um Ausnahmen zu vermeiden und ein Standardergebnis bereitzustellen:

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

Dies bewirkt den Rückgabetyp des Max Funktion lautet int?, wodurch das nullErgebnis zugelassen und ??das nullErgebnis durch ersetzt wird 0.


BEARBEITEN
Um etwas aus den Kommentaren zu verdeutlichen, unterstützt Entity Framework das asSchlüsselwort derzeit nicht. Die Vorgehensweise bei der Arbeit mit EF lautet also wie folgt :

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

Da [TypeOfWorkers]dies ein langer Klassenname sein könnte und das Schreiben mühsam ist, habe ich eine Erweiterungsmethode hinzugefügt, um zu helfen.

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

Dies funktioniert nur int , aber das gleiche könnte getan werden long, doubleoder jeder anderen Werttyp Sie benötigen. Die Verwendung dieser Erweiterungsmethode ist sehr einfach. Sie übergeben einfach Ihre Auswahlfunktion und geben optional einen Wert für null an, der standardmäßig 0 ist. Das oben Gesagte könnte also wie folgt umgeschrieben werden:

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

Hoffentlich hilft das den Leuten noch mehr.


3
Tolle Lösung! Die populärere DefaultIfEmptyAntwort funktioniert nur dann gut, wenn Maxkeine Bewertung durchgeführt wird.
McGuireV10

@ McGuireV10 Ja, ich mag es normalerweise nicht, Selectals Mittelsmann zu arbeiten, wenn ich nur eine Aggregatfunktion wie Maxim Ergebnis verwenden werde. Ich denke auch (ich habe dies noch nicht getestet), dass das generierte SQL eine zusätzliche Unterauswahlabfrage verwenden würde, während meine nur eine leere Menge behandeln würde, indem sie null zurückgibt. Danke für das Upvote und Feedback! ;)
CptRobby

@ McGuireV10 Ebenso würde ich, wenn das ShoeSizetatsächlich in einer verwandten UniformEntität wäre, nicht verwenden Workers.Where(x => x.CompanyId == 8).Select(x => x.Uniform).Max(x => x.ShoeSize), sondern nur die gesamte Auswertung in der MaxFunktion behalten : Workers.Where(x => x.CompanyId == 8).Max(x => x.Uniform.ShoeSize). Ich bevorzuge es, in meinen Abfragen so wenig Methoden wie möglich zu verwenden, damit EF die größte Freiheit bei der Entscheidung hat, wie Abfragen effizient erstellt werden sollen. ;-)
CptRobby

1
Ich konnte dies in EntityFramework nicht zum Laufen bringen. Kann jemand Licht ins Dunkel bringen? (Die Verwendung der DefaultIfEmpty-Technik hat funktioniert).
Moe Sisko

1
Eine generische Version der Erweiterungsmethode: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;
relativ


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

10

Wenn dies Linq to SQL ist, verwende ich es nicht gerne Any() da es zu mehreren Abfragen an SQL Server führt.

Wenn ShoeSizees sich nicht um ein nullbares Feld handelt, .Max(..) ?? 0funktioniert die Verwendung nur des Felds nicht, aber das Folgende funktioniert:

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

Das ausgegebene SQL wird absolut nicht geändert, aber es gibt 0 zurück, wenn die Sequenz leer ist, weil es das ändert Max() , um ein int?anstelle von einem zurückzugeben int.


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

(vorausgesetzt, das ShoeSizeist vom Typint )

Wenn Workerses sich um ein DbSetoder ObjectSetaus Entity Framework handelt, würde Ihre anfängliche Abfrage eine auslösen InvalidOperationException, sich jedoch nicht über eine leere Sequenz beschweren, sondern sich darüber beschweren, dass der materialisierte Wert NULL nicht in eine konvertiert werden kann int.


2

Max löst die System.InvalidOperationException aus. "Sequenz enthält keine Elemente."

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

Hinweis: Die Abfrage mit ist DefaultIfEmpty()möglicherweise erheblich langsamer . In meinem Fall war das eine einfache Abfrage mit .DefaultIfEmpty(DateTime.Now.Date).

Ich war zu faul, um es zu profilieren, aber offensichtlich hat EF versucht, alle Zeilen abzurufen und dann den Max()Wert zu übernehmen.

Fazit: Manchmal ist die Handhabung InvalidOperationExceptiondie bessere Wahl.


0

Sie können ein ternäres Element verwenden .Max(), um das Prädikat zu behandeln und seinen Wert festzulegen.

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

Sie müssten damit umgehen, dass die WorkersSammlung null / leer ist, wenn dies möglich ist, dies hängt jedoch von Ihrer Implementierung ab.


0

Sie können dies versuchen:

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

Ich denke, dies wird fehlschlagen, da Max ein int erwartet und es eine Null bekommt; Daher ist bereits ein Fehler aufgetreten, bevor der Null-Koaleszenz-Operator in Kraft tritt.
d219

0

Sie können überprüfen, ob Mitarbeiter vorhanden sind, bevor Sie Max () ausführen.

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);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.