Question

Nous travaillons sur une visionneuse de journaux.L'utilisation aura la possibilité de filtrer par utilisateur, gravité, etc.À l'époque de SQL, j'ajouterais à la chaîne de requête, mais je veux le faire avec Linq.Comment puis-je ajouter conditionnellement des clauses Where ?

Était-ce utile?

La solution

si vous souhaitez filtrer uniquement si certains critères sont remplis, faites quelque chose comme ceci

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

En procédant ainsi, votre arbre d'expression sera exactement ce que vous voulez.De cette façon, le SQL créé sera exactement ce dont vous avez besoin et rien de moins.

Autres conseils

Si vous devez filtrer la base sur une liste/un tableau, utilisez ce qui suit :

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

    }

J'ai fini par utiliser une réponse similaire à celle de Daren, mais avec une interface 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();

Cela crée la requête avant d'accéder à la base de données.La commande ne s'exécutera pas avant .ToList() à la fin.

En ce qui concerne le linq conditionnel, j'aime beaucoup le modèle de filtres et de tuyaux.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Fondamentalement, vous créez une méthode d'extension pour chaque cas de filtre qui prend en compte IQueryable et un paramètre.

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

Une autre option serait d'utiliser quelque chose comme le PredicateBuilder discuté ici.Il vous permet d'écrire du code comme celui-ci :

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;

Notez que je n'ai que cela pour fonctionner avec Linq 2 SQL.EntityFramework n'implémente pas Expression.Invoke, qui est requis pour que cette méthode fonctionne.J'ai une question concernant ce problème ici.

Ce faisant:

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

avoir ça dans le where déclaration:

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

signifie que lorsque la requête finale est créée, si lastNameSearch est false la requête omettra complètement tout code SQL pour la recherche de nom de famille.

J'ai résolu ce problème avec une méthode d'extension pour permettre à LINQ d'être activé conditionnellement au milieu d'une expression fluide.Cela supprime le besoin de diviser l'expression avec if déclarations.

.If() méthode d'extension :

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

Cela vous permet de faire ceci :

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

Voici également un IEnumerable<T> version qui gérera la plupart des autres expressions LINQ :

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

Ce n'est pas la plus jolie chose mais vous pouvez utiliser une expression lambda et transmettre vos conditions en option.Dans TSQL, je fais une grande partie des opérations suivantes pour rendre les paramètres facultatifs :

OÙ Champ = @FieldVar OU @FieldVar EST NULL

Vous pouvez dupliquer le même style avec le lambda suivant (un exemple de vérification d'authentification) :

MyDataContext db = new MyDataContext();

void RunQuery (string param1, string param2, int?paramètre3){

Func checkUser = utilisateur =>

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

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

((param3 != nul) ?utilisateur.Param3 == param3 :1 == 1);

Utilisateur foundUser = db.Users.SingleOrDefault(checkUser);

}

J'ai eu une exigence similaire récemment et j'ai finalement trouvé cela dans MSDN.Exemples CSharp pour Visual Studio 2008

Les classes incluses dans l'exemple DynamicQuery du téléchargement vous permettent de créer des requêtes dynamiques au moment de l'exécution au format suivant :

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

En utilisant cela, vous pouvez créer une chaîne de requête de manière dynamique au moment de l'exécution et la transmettre à la méthode Where() :

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

Utilisez simplement l'opérateur && de C# :

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

Modifier:Ah, il faut lire plus attentivement.Tu voulais savoir comment conditionnellement ajouter des clauses supplémentaires.Dans ce cas, je n'en ai aucune idée.:) Ce que je ferais probablement, c'est simplement préparer plusieurs requêtes et exécuter la bonne, en fonction de ce dont j'ai finalement besoin.

Vous pouvez utiliser une méthode externe :

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

...

bool ConditionalCheck( typeofRec input ) {
    ...
}

Cela fonctionnerait, mais ne peut pas être décomposé en arbres d'expression, ce qui signifie que Linq to SQL exécuterait le code de vérification sur chaque enregistrement.

Alternativement :

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

Cela pourrait fonctionner dans les arbres d'expression, ce qui signifie que Linq to SQL serait optimisé.

Eh bien, ce que je pensais, c'est que vous pourriez placer les conditions de filtre dans une liste générique de prédicats :

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

Cela donne une liste contenant "moi", "meyou" et "mow".

Vous pouvez optimiser cela en effectuant le foreach avec les prédicats dans une fonction totalement différente qui OU tous les prédicats.

Vous pouvez créer et utiliser cette méthode d'extension

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top