2009-03-07 12 views
2

Comment puis-je écrire une requête dynamique pour Linq, si je l'ai dis classe client qui détient les champs:requête dynamique dans LINQ

string name 
string address 
int phoneno 

Je dois interroger base des informations données similaires à

query = string.Empty; 

if(!string.IsNullorEmpty(name)) 
{ 
    query += "@name = name"; 
} 

if(!string.IsNullorEmpty(address)) 
{ 
    query += "@address = address"; 
} 

if(!string.IsNullorEmpty(phoneno)) 
{ 
    query += "@phoneno = phoneno"; 
} 

var result = from condition in customer 
    where(query) 
    select condition; 

Edit # 1:

les articles sont modifiables à l'exécution comme

private Customer[] GetCustomers(Dictionary<string,string> attributes) 
{ 
    here the attribute may be, name alone, or name and address, or name address and phoneno 


     foreach(string field in attributes.key) 
     { 
      query += field == attributes[key]; 

     } 

     Customers[] =ExecuteQuery(query); 

} 

Est-ce que ce type de requête est supporté par LINQ?

Edit # 2:

Salut Mouk,
Comme je suis nouveau à C#, je suis toujours en difficulté, cela ne fonctionne pas pour moi.

var query = _ConfigFile.ConnectionMasterSection; 

for(int i = 0; i < filter.count; i++) 
{ 
    query = result.Where(p => typeof(ConnectionMaster).GetProperty(filter[i].Attribute).Name == filter[i].Value); 
} 

Ce yeilds vide, alors que je l'ai utilisé cette

var query = _ConfigFile.ConnectionMasterSection; 

//Hard coded 
res.Where(q => q.category == filter[0].Value); 

Et cela a fonctionné comme je m'y attendais.

Salut Bryan Watts,
J'ai aussi essayé votre code et j'ai reçu cette erreur: "Lambda Parameter not in scope".

for(int i = 0; i < filter.count; i++) 
{ 
    Field item = filter[i]; 

    MemberExpression param = Expression.MakeMemberAccess(Expression.Parameter(typeof(Connection), "p"), typeof(Connection).GetProperty(item.Attribute)); 

    MemberExpression constant = Expression.MakeMemberAccess(Expression.Constant(item), typeof(Field).GetProperty("Value")); 
} 


try 
{ 
    var myquery = Queryable.Where(coll, Expression.Lambda<Func<Connection, bool>>(
    Expression.Equal(param, constant), Expression.Parameter(typeof(Connection),"p"))); 
} 

Quelle est l'erreur ici?

+0

Ajout d'un exemple à ma réponse qui devrait vous guider le reste du chemin. –

Répondre

5

Vérifiez ce http://www.albahari.com/nutshell/predicatebuilder.aspx, cela permet de construire des prédicats fortement typés, ça peut être vraiment sympa. Si vous voulez réellement des prédicats construits dynamiques de chaîne que vous pouvez utiliser le LINQ Dynamic Query Library fourni par ScottGu.

Les deux accompliront ce que vous voulez bien que je recommanderais la première option avant la seconde.

Permettant de faire:

var predicate = PredicateBuilder.True<MyLinqType>(); 

if(!string.IsNullOrEmpty(name)) 
    predicate = predicate.And(p => p.name == name); 


... 

var myResults = Context.MyLinTypeQueryTable.Where(predicate); 

Et plus.

3

Ici, vous allez:

var result = from customer in Customers 
      where string.IsNullOrEmpty(phoneNo) || customer.PhoneNo == phoneNo 
      where string.IsNullOrEmpty(address) || customer.Address == address 
      select customer; 

Si vous craignez que cela génère la requête SQL optimale en dessous, comme toujours, vous devez joindre un Analyseur de requêtes SQL et vérifier. Mais je crois que l'analyseur d'expression dans Linq To Sql va réduire les clauses where en fonction de la valeur des arguments.

0

Vous pouvez utiliser l'interface fluide et ajouter une nouvelle clause Where pour chaque condition. Quelque chose comme:

var result = from cus in customers select cus; 
if(!string.IsNullOrEmpty(name)) 
     result= result.Where(p => p.Name == name); 

EDIT sur un commentaire hte:

si vous interrogez sur une collection en mémoire, vous pouvez récupérer les propriétés à l'aide de réflexion.Supposons que GetProperty récupère la propriété par réflexion. En utilisant Linq2Sql, cette méthode aura pour résultat de récupérer tous les enregistrements et de les répéter en utilisant la réflexion.

0

Voir la modification n ° 1 dans la question d'origine.

+0

Oui, c'est via la bibliothèque LINQ Dynamic Query Library I liée. Vous devrez juste construire la chaîne. Juste une note de côté, vous ne devriez pas ajouter une réponse qui n'est pas une réponse car ce n'est pas un forum, éditez votre question originale pour inclure des changements. –

0

Il semble que vous ayez besoin de composer dynamiquement des requêtes.

Voir my answer à this question.

Il explique comment les requêtes sur un IQueryable<T> sont composées par le compilateur, et ce que vous pouvez faire pour ajouter des éléments dynamiques.

Modifier

Voici un exemple de la façon dont vous construire dynamiquement un ensemble de conditions Where au-dessus d'un IQueryable<Customer>:

// This method ANDs equality expressions for each property, like so: 
// 
// customers.Where(c => c.Property1 == value1 && c.Property2 == value2 && ...); 

private IQueryable<Customer> FilterQuery(IQueryable<Customer> customers, IDictionary<string, string> filter) 
{ 
    var parameter = Expression.Parameter(typeof(Customer), "c"); 
    Expression filterExpression = null; 

    foreach(var filterItem in filter) 
    { 
     var property = typeof(Customer).GetProperty(filterItem.Key); 
     var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     var equality = Expression.Equal(propertyAccess, Expression.Constant(filterItem.Value)); 

     if(filterExpression == null) 
     { 
      filterExpression = equality; 
     } 
     else 
     { 
      filterExpression = Expression.And(filterExpression, equality); 
     } 
    } 

    if(filterExpression != null) 
    { 
     var whereBody = Expression.Lambda<Func<Customer, bool>>(filterExpression, parameter); 

     customers = customers.Where(whereBody); 
    } 

    return customers; 
} 
0

J'ai eu une bonne expérience avec Dynamic LINQ. Je l'ai utilisé pour un tableau HTML riche qui pourrait être filtré et trié côté serveur. Le serveur reçoit une requête contenant un paramètre de requête où la clé est le nom de la propriété (par exemple 'Lastname') et la valeur est la valeur sur laquelle la propriété doit être triée (par exemple 'Smith'). À l'aide de ces informations, j'ai créé une chaîne de requête que j'ai transmise à la méthode Where de Dynamic LINQ.

En gros, vous pourriez penser à quelque chose comme ce qui suit:

public static IQueryable<T> Filter<T>(this IQueryable<T> query, Dictionary<string, string> dictionary) 
{ 
    Type t = typeof(T); 
    StringBuilder sb = new StringBuilder(); 
     PropertyInfo[] properties = t.GetProperties(); 
     foreach(string key in dictionary.Keys) 
     { 
      PropertyInfo property = properties.Where(p => p.Name == key).SingleOrDefault(); 
      if(property != null) 
      { 
       if (sb.Length > 0) sb.Append(" && "); 

       string value = dictionary[key]; 

       sb.Append(string.Format(@"{0}.ToString().Contains(""{1}"")", key, value)); 
      } 
     } 

     if (sb.Length > 0) 
     return query.Where(sb.ToString()); 
    else 
      return query; 
} 

Le code est sur le haut de ma tête et donc non testé.

Bien sûr, c'est la version la plus basique: elle fait une simple comparaison de chaînes. Si vous voulez avoir une comparaison numérique (ce qui signifie que vous voulez par exemple l'utilisateur où UserID est exactement 100, pas le UserID.ToString().Contains("100")), ou interroger des propriétés imbriquées (Customer.Company.CompanyAddress par exemple), ou des collections de requêtes cela devient plus compliqué. Vous devriez également penser à la sécurité: alors que Dynamic LINQ n'est pas vulnérable à l'injection SQL, vous ne devez pas le laisser aveuglément analyser toutes les entrées utilisateur.