Frage

Wir arbeiten an einem Log Viewer.Der Benutzer hat die Möglichkeit, nach Benutzer, Schweregrad usw. zu filtern.In den SQL-Tagen habe ich die Abfragezeichenfolge ergänzt, aber ich möchte es mit Linq machen.Wie kann ich Where-Klauseln bedingt hinzufügen?

War es hilfreich?

Lösung

Wenn Sie nur filtern möchten, wenn bestimmte Kriterien erfüllt sind, gehen Sie etwa so vor

var logs = from log in context.Logs
           select log;

if (filterBySeverity)
    logs = logs.Where(p => p.Severity == severity);

if (filterByUser)
    logs = logs.Where(p => p.User == user);

Auf diese Weise wird Ihr Ausdrucksbaum genau Ihren Wünschen entsprechen.Auf diese Weise wird das erstellte SQL genau das sein, was Sie benötigen, und nicht weniger.

Andere Tipps

Wenn Sie basierend auf einer Liste/Array filtern müssen, verwenden Sie Folgendes:

    public List<Data> GetData(List<string> Numbers, List<string> Letters)
    {
        if (Numbers == null)
            Numbers = new List<string>();

        if (Letters == null)
            Letters = new List<string>();

        var q = from d in database.table
                where (Numbers.Count == 0 || Numbers.Contains(d.Number))
                where (Letters.Count == 0 || Letters.Contains(d.Letter))
                select new Data
                {
                    Number = d.Number,
                    Letter = d.Letter,
                };
        return q.ToList();

    }

Am Ende habe ich eine Antwort ähnlich der von Daren verwendet, jedoch mit einer IQueryable-Schnittstelle:

IQueryable<Log> matches = m_Locator.Logs;

// Users filter
if (usersFilter)
    matches = matches.Where(l => l.UserName == comboBoxUsers.Text);

 // Severity filter
 if (severityFilter)
     matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);

 Logs = (from log in matches
         orderby log.EventTime descending
         select log).ToList();

Dadurch wird die Abfrage aufgebaut, bevor die Datenbank erreicht wird.Der Befehl wird erst mit .ToList() am Ende ausgeführt.

Wenn es um bedingtes Linq geht, mag ich das Filter- und Pipe-Muster sehr.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Grundsätzlich erstellen Sie für jeden Filterfall eine Erweiterungsmethode, die IQueryable und einen Parameter aufnimmt.

public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
    return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}

Eine andere Möglichkeit wäre, so etwas wie den besprochenen PredicateBuilder zu verwenden Hier.Es ermöglicht Ihnen, Code wie den folgenden zu schreiben:

var newKids  = Product.ContainsInDescription ("BlackBerry", "iPhone");

var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
                  .And (Product.IsSelling());

var query = from p in Data.Products.Where (newKids.Or (classics))
            select p;

Beachten Sie, dass dies nur mit Linq 2 SQL funktioniert.EntityFramework implementiert Expression.Invoke nicht, was für die Funktion dieser Methode erforderlich ist.Ich habe eine Frage zu diesem Thema Hier.

Dies tun:

bool lastNameSearch = true/false; // depending if they want to search by last name,

dies in der where Stellungnahme:

where (lastNameSearch && name.LastNameSearch == "smith")

bedeutet, dass beim Erstellen der endgültigen Abfrage if lastNameSearch Ist false Bei der Abfrage wird jegliches SQL für die Nachnamensuche vollständig weggelassen.

Ich habe dieses Problem mit einer Erweiterungsmethode gelöst, die es ermöglicht, LINQ mitten in einem fließenden Ausdruck bedingt zu aktivieren.Dadurch entfällt die Notwendigkeit, den Ausdruck mit aufzubrechen if Aussagen.

.If() Erweiterungsmethode:

public static IQueryable<TSource> If<TSource>(
        this IQueryable<TSource> source,
        bool condition,
        Func<IQueryable<TSource>, IQueryable<TSource>> branch)
    {
        return condition ? source : branch(source);
    }

Dies ermöglicht Ihnen Folgendes:

return context.Logs
     .If(filterBySeverity, q => q.Where(p => p.Severity == severity))
     .If(filterByUser, q => q.Where(p => p.User == user))
     .ToList();

Hier ist auch ein IEnumerable<T> Version, die die meisten anderen LINQ-Ausdrücke verarbeiten kann:

public static IEnumerable<TSource> If<TSource>(
    this IEnumerable<TSource> source,
    bool condition,
    Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
    {
        return condition ? source : branch(source);
    }

Es ist nicht das Schönste, aber Sie können einen Lambda-Ausdruck verwenden und Ihre Bedingungen optional übergeben.In TSQL mache ich viele der folgenden Schritte, um Parameter optional zu machen:

WHERE Field = @FieldVar ODER @FieldVar IST NULL

Sie könnten denselben Stil mit dem folgenden Lambda duplizieren (ein Beispiel für die Überprüfung der Authentifizierung):

MyDataContext db = new MyDataContext();

void RunQuery(string param1, string param2, int?param3){

Funktion checkUser = user =>

((param1.Length > 0)?user.Param1 == param1 :1 == 1) &&

((param2.Length > 0)?user.Param2 == param2 :1 == 1) &&

((param3 != null)?user.Param3 == param3 :1 == 1);

BenutzerfoundUser = db.Users.SingleOrDefault(checkUser);

}

Ich hatte kürzlich eine ähnliche Anforderung und habe diese schließlich im MSDN gefunden.CSharp-Beispiele für Visual Studio 2008

Mit den im DynamicQuery-Beispiel des Downloads enthaltenen Klassen können Sie zur Laufzeit dynamische Abfragen im folgenden Format erstellen:

var query =
db.Customers.
Where("City = @0 and Orders.Count >= @1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");

Damit können Sie zur Laufzeit dynamisch eine Abfragezeichenfolge erstellen und diese an die Where()-Methode übergeben:

string dynamicQueryString = "City = \"London\" and Order.Count >= 10"; 
var q = from c in db.Customers.Where(queryString, null)
        orderby c.CompanyName
        select c;

Verwenden Sie einfach den &&-Operator von C#:

var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")

Bearbeiten:Ah, ich muss genauer lesen.Sie wollten wissen, wie es geht bedingt zusätzliche Klauseln hinzufügen.In dem Fall habe ich keine Ahnung.:) Was ich wahrscheinlich tun würde, wäre, einfach mehrere Abfragen vorzubereiten und die richtige auszuführen, je nachdem, was ich am Ende brauchte.

Sie könnten eine externe Methode verwenden:

var results =
    from rec in GetSomeRecs()
    where ConditionalCheck(rec)
    select rec;

...

bool ConditionalCheck( typeofRec input ) {
    ...
}

Dies würde funktionieren, kann aber nicht in Ausdrucksbäume zerlegt werden, was bedeutet, dass Linq to SQL den Prüfcode für jeden Datensatz ausführen würde.

Alternative:

var results =
    from rec in GetSomeRecs()
    where 
        (!filterBySeverity || rec.Severity == severity) &&
        (!filterByUser|| rec.User == user)
    select rec;

Das könnte in Ausdrucksbäumen funktionieren, was bedeutet, dass Linq to SQL optimiert wäre.

Nun, ich dachte, Sie könnten die Filterbedingungen in eine generische Liste von Prädikaten einfügen:

    var list = new List<string> { "me", "you", "meyou", "mow" };

    var predicates = new List<Predicate<string>>();

    predicates.Add(i => i.Contains("me"));
    predicates.Add(i => i.EndsWith("w"));

    var results = new List<string>();

    foreach (var p in predicates)
        results.AddRange(from i in list where p.Invoke(i) select i);               

Das Ergebnis ist eine Liste mit „me“, „meyou“ und „mow“.

Sie könnten dies optimieren, indem Sie foreach mit den Prädikaten in einer völlig anderen Funktion ausführen, die alle Prädikate ODER-verknüpft.

Sie können diese Erweiterungsmethode erstellen und verwenden

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top