Domanda

Stiamo lavorando su un visualizzatore di log.L'utente avrà la possibilità di filtrare per utente, gravità, ecc.Ai tempi di Sql aggiungerei alla stringa di query, ma voglio farlo con Linq.Come posso aggiungere condizionalmente clausole where?

È stato utile?

Soluzione

se vuoi filtrare solo se vengono soddisfatti determinati criteri, fai qualcosa del genere

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);

In questo modo il tuo albero delle espressioni sarà esattamente quello che desideri.In questo modo l'SQL creato sarà esattamente ciò di cui hai bisogno e niente di meno.

Altri suggerimenti

Se è necessario filtrare in base a un elenco/array, utilizzare quanto segue:

    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();

    }

Ho finito con una risposta simile a quella di Daren, ma con un'interfaccia IQueryable:

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();

Ciò crea la query prima di colpire il database.Il comando non verrà eseguito fino a .ToList() alla fine.

Quando si tratta di linq condizionale, mi piacciono molto i filtri e lo schema dei tubi.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Fondamentalmente crei un metodo di estensione per ogni caso di filtro che accetta IQueryable e un parametro.

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

Un'altra opzione sarebbe quella di utilizzare qualcosa come il PredicateBuilder discusso Qui.Ti consente di scrivere codice come il seguente:

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;

Tieni presente che riesco a farlo funzionare solo con Linq 2 SQL.EntityFramework non implementa Expression.Invoke, necessario per il funzionamento di questo metodo.Ho una domanda riguardo a questo problema Qui.

Facendo questo:

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

avendo questo in where dichiarazione:

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

significa che quando viene creata la query finale, if lastNameSearch È false la query ometterà completamente qualsiasi SQL per la ricerca del cognome.

Ho risolto questo problema con un metodo di estensione per consentire l'abilitazione condizionale di LINQ nel mezzo di un'espressione fluente.Ciò elimina la necessità di spezzare l'espressione con if dichiarazioni.

.If() metodo di estensione:

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

Questo ti permette di fare questo:

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

Ecco anche un IEnumerable<T> versione che gestirà la maggior parte delle altre espressioni LINQ:

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

Non è la cosa più carina ma puoi usare un'espressione lambda e passare le tue condizioni facoltativamente.In TSQL faccio molte delle seguenti operazioni per rendere i parametri facoltativi:

DOVE Campo = @FieldVar OR @FieldVar È NULL

Potresti duplicare lo stesso stile con il seguente lambda (un esempio di controllo dell'autenticazione):

MyDataContext db = new MyDataContext();

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

Funz checkUser = utente =>

((param1.Lunghezza > 0)?utente.Param1 == param1 :1 == 1) &&

((param2.Lunghezza > 0)?utente.Param2 == param2 :1 == 1) &&

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

Utente trovatoUser = db.Users.SingleOrDefault(checkUser);

}

Recentemente ho avuto un requisito simile e alla fine l'ho trovato in MSDN.Esempi CSharp per Visual Studio 2008

Le classi incluse nell'esempio DynamicQuery del download consentono di creare query dinamiche in fase di runtime nel seguente formato:

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

Usandolo puoi creare una stringa di query dinamicamente in fase di esecuzione e passarla al metodo Where():

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

Basta usare l'operatore && di C#:

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

Modificare:Ah, devo leggere con più attenzione.Volevi sapere come farlo condizionatamente aggiungere ulteriori clausole.In tal caso, non ne ho idea.:) Quello che probabilmente farei è semplicemente preparare diverse query ed eseguire quella giusta, a seconda di ciò di cui ho bisogno.

Potresti usare un metodo esterno:

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

...

bool ConditionalCheck( typeofRec input ) {
    ...
}

Funzionerebbe, ma non può essere suddiviso in alberi delle espressioni, il che significa che Linq to SQL eseguirà il codice di controllo su ogni record.

In alternativa:

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

Ciò potrebbe funzionare negli alberi delle espressioni, il che significa che Linq to SQL sarebbe ottimizzato.

Bene, quello che pensavo era che potresti inserire le condizioni del filtro in un elenco generico di Predicati:

    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);               

Ciò si traduce in un elenco contenente "me", "meyou" e "mow".

Potresti ottimizzarlo eseguendo foreach con i predicati in una funzione completamente diversa che esegue l'OR di tutti i predicati.

È possibile creare e utilizzare questo metodo di estensione

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top