it-swarm.com.de

LINQ: Hinzufügen der where-Klausel nur, wenn ein Wert nicht null ist

Ich weiß, ein typischer Weg ist wie folgt:

IQueryable query = from staff in dataContext.Staffs;
if(name1 != null)
{
     query = from staff in query where (staff.name == name1);
}

In einem Programm, das wir von anderen Entwicklern übernommen haben, haben wir jedoch Code wie diesen gesehen:

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);

Wenn dies eine normale SQL-Anweisung ist, würde ich definitiv sagen, dass die zweite eine schlechte Praxis ist. Weil es der Abfrage eine bedeutungslose where-Klausel hinzufügt, wenn name1 den Wert null hat.

Aber ich bin neu in LINQ, also bin ich mir nicht sicher, ob LINQ anders ist? 

19
Henry

du kannst es gerne schreiben

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);

Auf diese Weise wird der zweite Teil Ihrer Bedingung nicht bewertet, wenn Ihre erste Bedingung als falsch bewertet wird

Update:
wenn Sie schreiben

IQueryable query = from staff in dataContext.Staffs;
    query = from staff in query where (name1 == null || staff.name == name1);

und name1 ist null Der zweite Teil Ihrer Bedingung wird nicht ausgewertet, da Bedingung nur eine Bedingung erfordert, um wahr zurückzugeben

für weitere Details siehe hier link

18

Häufig fühlt sich diese Art von Schreiben glatter an, wenn sie anstelle der Abfragesyntax die fließende Syntax verwendet.

z.B.

IQueryable query = dataContext.Staffs;
if(name1 != null)
{
     query = query.Where(x => x.name == name1);
}

Wenn also name1 null ist, führen Sie einfach keinen Where()-Aufruf aus. Wenn Sie über mehrere verschiedene Filter verfügen, die möglicherweise alle erforderlich sind oder nicht, und möglicherweise verschiedene Sortierreihenfolgen, ist dies meiner Ansicht nach viel einfacher zu handhaben.

Edit for alex: OK, ich habe die Frage zum Hinzufügen einer where-Klausel nur beantwortet, wenn ein Wert nicht null ist. Als Antwort auf den anderen Teil der Frage habe ich dies mit Entity Framework 4 ausprobiert, um zu sehen, welche SQL-Dateien von LINQ erzeugt wurden. Sie tun dies, indem Sie query in eine ObjectQuery umwandeln und .ToTraceString() aufrufen. Die Ergebnisse lauteten, dass die WHERE-Klausel wie folgt ausgegeben wurde:

WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1

Ja, es ist klassisches falsches SQL. Wenn Sie einen Index für die Spalte name haben, erwarten Sie nicht, dass er verwendet wird.

Edit # 2: Versuchte dies erneut mit LINQ to SQL anstelle von Entity Framework, mit ziemlich unterschiedlichen Ergebnissen. Wenn Sie diesmal die Abfrage mit name1 als null versuchen, erhalten Sie, wie Sie hoffen, keine WHERE-Klausel. Wenn Sie name1 als "a" versuchen, wurde ein einfacher WHERE [t0].[name] = @p0 und @p0 als "a" gesendet. Entity Framework scheint also nicht zu optimieren. Das ist ein bisschen besorgniserregend.

10
Carson63000

Am besten erstellen Sie sich eine Erweiterungsmethode , die eine Bedingungsanweisung und einen where-Ausdruck enthält. Wenn die Bedingung erfüllt ist, wird der where-Ausdruck verwendet. Andernfalls wird sie nicht verwendet. Dies kann Ihren Code drastisch bereinigen, so dass keine if-Anweisungen erforderlich sind. 

public static class LinqExtensions
{
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}

Jetzt kannst du deinen Code so schreiben:

IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
3
Soto

LINQ unterscheidet sich in einigen anderen Ursachen (nicht in diesen Ursachen). LINQ ist der Weg, um Daten mit einem kleinen Code und möglichst klarem Code auf die "schnellere Art" abzurufen, da LINQ viele Vorteile bietet:

  1. Erleichtert die Umwandlung von Daten in Objekte. Ich bin sicher, Sie haben gehört, dass der Begriff "Impedence Mismatch" häufig verwendet wird. Das bedeutet, dass LINQ die Menge an Arbeit reduziert, die Sie zum Übersetzen zwischen objektorientiertem Code und Datenparadigmen wie hierarchischen, flachen Dateien, Nachrichten usw. relational und mehr. Das "Impedence Mismatch" wird nicht beseitigt, da Sie Ihre Daten immer noch in ihrer ursprünglichen Form angeben müssen, aber die Brücke von hier nach dort (IMO) ist viel kürzer.

  2. Eine allgemeine Syntax für alle Daten. Wenn Sie die Abfragesyntax gelernt haben, können Sie sie mit jedem LINQ-Anbieter verwenden. Ich denke, das ist ein viel besseres Entwicklungsparadigma als der Tower of Babel, der im Laufe der Jahre mit Datenzugriffstechnologien gewachsen ist. Natürlich verfügt jeder LINQ-Anbieter über eindeutige Nuancen, aber der grundlegende Ansatz und die Abfragesyntax sind die gleichen.

  3. Stark getippter Code. Die Abfragesyntax C # (oder VB.NET) ist Teil der Sprache, und Sie codieren mit C # -Typen, die in etwas übersetzt werden, das ein Anbieter versteht. Dies bedeutet, dass Sie die Produktivität steigern, wenn Ihr Compiler Fehler früher im Entwicklungslebenszyklus findet als anderswo. Zugegeben, viele Fehler in der gespeicherten proc-Syntax erzeugen beim Speichern Fehler, aber LINQ ist allgemeiner als SQL Server. Sie müssen an alle anderen Arten von Datenquellen denken, die Laufzeitfehler generieren, da ihre Abfragen mit Zeichenfolgen oder einem anderen lose typisierten Mechanismus gebildet werden.

  4. Provider-Integration Das Zusammenführen von Datenquellen ist sehr einfach. Beispielsweise können Sie LINQ to Objects, LINQ to SQL und LINQ to XML zusammen für einige sehr ausgefeilte Szenarien verwenden. Ich finde es sehr elegant.

  5. Reduktion der Arbeit. Vor LINQ habe ich viel Zeit damit verbracht, DALs zu erstellen, aber jetzt ist mein DataContext die DAL. Ich habe auch OPFs verwendet, aber jetzt habe ich LINQ, das mit mehreren Anbietern und vielen anderen Drittanbietern ausgeliefert wird, was mir die Vorteile meiner vorherigen Punkte bringt. Ich kann in einer Minute einen LINQ to SQL DataContext einrichten (so schnell, wie mein Computer und IDE mithalten können).

  6. Die Leistung im allgemeinen Fall wird nicht zum Problem. SQL Server optimiert Abfragen heutzutage ziemlich genau wie gespeicherte Prozeduren. Natürlich gibt es immer noch Fälle, in denen gespeicherte Prozeduren aus Leistungsgründen erforderlich sind. Beispielsweise habe ich es als intelligenter empfunden, eine gespeicherte Prozedur zu verwenden, wenn ich mehrere Interaktionen zwischen Tabellen mit zusätzlicher Logik in einer Transaktion hatte. Der Kommunikationsaufwand beim Versuch, dieselbe Aufgabe im Code auszuführen, zusätzlich zu einer Einbindung des DTC in eine verteilte Transaktion, machte die Entscheidung für eine gespeicherte Prozedur zwingender. Für eine Abfrage, die in einer einzelnen Anweisung ausgeführt wird, ist LINQ jedoch meine bevorzugte Wahl, da selbst bei einem geringen Leistungsgewinn durch eine gespeicherte Prozedur die Vorteile der vorherigen Punkte (IMO) mehr Gewicht haben.

  7. Eingebaute Sicherheit. Ein bevorzugter Grund für die Speicherung gespeicherter Prozeduren vor LINQ war, dass die Verwendung von Parametern erzwungen wurde, um SQL-Injection-Angriffe zu reduzieren. LINQ to SQL parametrisiert bereits die Eingabe, was ebenso sicher ist.

  8. LINQ ist deklarativ. Der Arbeit mit LINQ to XML oder LINQ to SQL wird viel Aufmerksamkeit gewidmet, aber LINQ to Objects ist unglaublich leistungsfähig. Ein typisches Beispiel für LINQ to Objects ist das Lesen von Elementen aus einer Zeichenfolge []. Dies ist jedoch nur ein kleines Beispiel. Wenn Sie über alle IEnumerable-Sammlungen nachdenken (Sie können auch IEnumerable abfragen), mit denen Sie täglich arbeiten, sind die Möglichkeiten ausreichend. d. h. Durchsuchen eines ASP.NET-ListBox-Steuerelements nach ausgewählten Elementen, Durchführen von Satzoperationen (z. B. Union) für zwei Sammlungen oder Durchlaufen einer Liste und Ausführen eines Lambda in einem ForEach-Element. Wenn Sie anfangen, in LINQ zu denken, das in der Natur deklarativ ist, können Sie feststellen, dass viele Ihrer Aufgaben einfacher und intuitiver sind als die heute verwendeten imperativen Techniken.

Ich könnte wahrscheinlich weitermachen, aber ich sollte dort besser aufhören. Es ist zu hoffen, dass dies eine positivere Einschätzung darüber ergibt, wie Sie mit LINQ produktiver arbeiten und es vielleicht als eine nützliche Technologie aus einer breiteren Perspektive betrachten.

1
Jacob

Also habe ich die hier angegebene .Where(..., x => ...)-Erweiterungsmethode als Antwort ausprobiert, aber sie funktioniert nicht gegen Entity Framework, da Linq To Entities nicht weiß, wie man das in TSQL übersetzt. 

Hier ist meine Lösung, die meine Funktion abruft:

Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue))
{
    columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue;
}

_context.SomeEfPocos.Where(x => ..... &&
            ..... &&
            ..... &&)
.Where(columnBeingFilteredPredicate);

someColumnBeingFilteredValue ist in meinem Fall ein Zeichenfolgeparameter für die Einkapselungsmethode mit dem Standardwert NULL.

1
Stephen York

Ich habe dieses Muster in Standard-SQL gesehen und es scheint nützlich zu sein, wenn Sie mehrere Parameter haben, die NULL sein können. Zum Beispiel:

SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
                       AND ( @LastName IS NULL OR LastName = @LastName )

Wenn Sie dies in LINQ sehen, haben sie möglicherweise ihre alten SQL-Abfragen blind übersetzt.

1
Danko Durbić

Ich benutze gerne den Ausdruck .__ z.B.

    Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true;

    if (DateBirth.HasValue)
                {
                    Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth);
                    expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate);
                }

IQueryable query = dataContext.Persons;
 query = query.Where(expresionFinal);
1
J4ime

Für EF Core habe ich es so getrennt:

IQueryable<Partners> recs = contextApi.Partners;
if (status != -1)
{
   recs = recs.Where(i => i.Status == status);
}
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName);
foreach (var rec in recs)
{
}

Ich musste mit meiner Eingabe explizit sein, statt auf var zu vertrauen.

0
Chad Kuehn

Nein, ich bin mit Ihnen nicht sehr einverstanden 

if(name1 != null)
// do your stuff

aber was passiert, wenn Sie mit name1 etwas anderes machen, das einen Nullwert hat .. !! Ok, betrachten Sie diese Situation . In diesem Beispiel zeigen Sie, wie Sie mit möglichen Nullwerten in Quellensammlungen umgehen. Eine Objektauflistung wie IEnumerable<T> kann Elemente enthalten, deren Wert null ist .. Wenn eine Quellensammlung null ist oder ein Element enthält, dessen Wert null ist, Und Ihre Abfrage keine Nullwerte verarbeitet, wird eine NullReferenceException verwendet geworfen, wenn Sie die Abfrage ausführen.

Wahrscheinlich könnte dies ein Problem sein ... 

0
PawanS