2017-02-14 2 views
-1

J'ai un code qui recherche dans une table de données des chaînes spécifiques et retourne un sous-ensemble de la datatable entière en fonction de certaines conditions (champ id unique). Exemple de code ci-dessous qui recherche "First" et "Employee" dans un datatable et ne renvoie que les entrées qui ont la même valeur l_id. Maintenant, si j'obtiens le texte de recherche de manière dynamique, comment puis-je procéder avec cela? Pour. Par exemple, dans le code ci-dessus si je devais passer "Premier" ainsi que "Employé" ainsi que "salaire" alors comment procéder?Construire dynamiquement la clause 'where' dans Linq

Comment puis-je rendre ce générique?

Merci d'avance pour votre aide!

EDIT - On dirait que je ne l'ai pas été clair dans mon post, alors laissez-moi reformuler à nouveau

J'ai un datatable qui ressemble à ce

l | t | r | b | x | y | w_text | l_id 
------------------------------------------------------------- 
70 | 314 | 141 | 328 | 1 | 5 | First | 4 
149 | 318 | 194 | 328 | 2 | 5 | Employe| 4 
204 | 311 | 254 | 326 | 3 | 5 | John | 4 
264 | 311 | 325 | 326 | 4 | 5 | Smith | 4 
1924 | 310 | 2000 | 329 | 5 | 5 | First | 5 
70 | 341 | 109 | 355 | 1 | 6 | step | 5 
115 | 340 | 130 | 355 | 2 | 6 | of  | 5 
136 | 340 | 175 | 355 | 3 | 6 | Linq | 5 
185 | 339 | 320 | 356 | 4 | 6 | Last | 6 
70 | 394 | 101 | 411 | 1 | 8 | Employe| 6 
114 | 390 | 199 | 405 | 2 | 8 | John | 6 
210 | 390 | 269 | 405 | 3 | 8 | Doe | 6 

Les seuls critères de recherche que j'ai sur ma main est 'W_Text'.So je voudrais rechercher dire l'expression unique "Premier employé" .Seulement un l_id (dans ce cas l_id = 4) aurait à la fois les mots "First" ainsi que "Employee" .Si je recherche "Premier" séparément et "Employé" séparément, alors j'obtiendrais un ensemble de données plus grand qui ne résout pas mon but. Mon objectif est d'obtenir l'ensemble de données ci-dessous unique lorsque je recherche « premier employé »

l | t | r | b | x | y | w_text | l_id 
------------------------------------------------------------- 
70 | 314 | 141 | 328 | 1 | 5 | First | 4 
149 | 318 | 194 | 328 | 2 | 5 | Employe| 4 
204 | 311 | 254 | 326 | 3 | 5 | John | 4 
264 | 311 | 325 | 326 | 4 | 5 | Smith | 4 

En termes SQL, ce qui est similaire à

Select * From Table where l_id in (Select l_id from Table where W_Text in ('First','Employee') group by l_id having count(l_id) > 1) 

Le code ci-dessus que je l'ai mentionné (a été aidé par un bon samaritain) fonctionne parfaitement bien et me renvoie l'ensemble de données ci-dessus. Le problème est que cela ne fonctionne qu'avec "First Employee". J'ai des problèmes avec la recherche dire "Première étape de Linq". la phrase de recherche est transmise au programme lors de l'exécution et peut être de combien de mots jamais. J'ai essayé de séparer les Wheres mais la condition 'ayant' manque et c'est là que le jeu de données entier est à nouveau retourné.

Par conséquent, je vous demande à tous de bien vouloir m'aider avec ce problème. Je suis très nouveau à Linq et j'essaie de faire mon chemin. En attendant, toute aide que je pourrais obtenir serait grandement appréciée. Merci.

--- EDIT a ce travail en utilisant ce code (avec l'aide de quelqu'un)

List<string> wTextFilter = new List<string>(); 

     foreach (string sf in strInputString.Split(' ')) //array by splitting on white space 
     { 
      wTextFilter.Add(sf); 
     } 

     // Get all Id's that satisfy all conditions:    
     List<int> results = dtResult.AsEnumerable() 
      // Get all Id's: 
      .Select(dataRow => dataRow.Field<int>("l_id")) 
      // Filter the Id's : 
      .Where(id => 
       // the Id should be greater than one. 
        id > 1 && 
         // check if all W_Text entries has a record in the datatable with the same Id. 
        wTextFilter.All(W_Text => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == W_Text && dataRow.Field<int>("l_id") == id))) 
        .Distinct().ToList(); 

     // Get all datatable rows filtered by the list of Id's. 
     dtCopy = dtResult.AsEnumerable().Where(dataRow => results.Contains((dataRow.Field<int>("l_id")))).CopyToDataTable();  
+0

utilisez votre propre homme logique! –

+1

Pas tout à fait sûr de ce dont vous avez besoin, mais vous pouvez enchaîner les appels Where, comme '.Where (condition1) .Where (condition2)' et ainsi de suite. – vesan

+0

Merci mon pote, pour le conseil. Je suis incapable de le craquer, d'où un avis d'expert! – DevNovice

Répondre

2

Peu de problèmes avec votre code:

  1. vous sélectionnez l'ID avant vous » ve filtré. Cela signifie que vous vous retrouvez avec une collection de int s, ce qui signifie que vous ne pouvez pas filtrer par une autre colonne. Vous devez filtrer puis sélectionner les colonnes requises

  2. Vous n'avez pas besoin de la dernière vérification pour voir si la colonne l_id. Nous avons déjà vérifié que l_id == id, donc évidemment la colonne existe

  3. Votre requête actuelle est incorrecte.Vous retournez la ligne si toute ligne dans l'ensemble de données correspond à:

dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "First" && dataRow.Field<int>("l_id") == id) 

Cela dit, pour chaque ligne, vérifiez s'il y a des des lignes correspondant à. Si c'est le cas, renvoyez cette ligne. Votre requête retournera l'intégralité du jeu de données, ou rien. Vous pouvez enchaîner .Where() clauses. Par exemple:

public List<int> DoIt(int id, params string[] searchFor) 
{ 
    var results = dtResult.AsEnumerable()  
     // Filter the Id's : 
     .Where(dr => dr.id > 1) 
     .Where(dr => dr.Field<int>("l_id") == id); 

    foreach (var sf in searchFor) 
     results = results.Where(dr => dr.Field<string>("W_Text") == sf); 

    results = results.Select(dataRow => dataRow.Field<int>("l_id")) 

    return results.Distinct().CopyToDataTable(); 
} 
+0

Rob. Merci pour votre réponse. Je pense que je n'étais pas clair dans ce que je voulais. J'ai ajouté un Edit sur l'OP. S'il vous plaît pouvez-vous jeter un coup d'oeil? Merci La chose est que je suis en train de perdre sur le jeu de résultats si je n'utilise pas le && qui est crucial dans ce cas – DevNovice

+0

Pouvez-vous fusionner deux Où ensemble? Dites '. Où (dr => dr.id> 1 && dr => dr.Field (" l_id ") == id)'. – Bigeyes

1

Commencez avec votre clause de base:

results = results.Where(id => id > 1); 

Puis ajouter dynamiquement des clauses au besoin:

if (/**some condition**/) 
    results = results.Where(id => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "First" && dataRow.Field<int>("l_id") == id)); 
if (/**another condition**/) 
    results = results.Where(id => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "Employee" && dataRow.Field<int>("l_id") == id)) 

et ainsi de suite. Pour chaque condition que vous souhaitez ajouter dynamiquement, ajoutez une nouvelle clause .Where(). Vous pouvez en chaîner autant que vous le souhaitez, et logiquement ce serait la même chose qu'une série de clauses && en un seul .Where().

+0

David -. Merci pour votre réponse. Je pense que je n'étais pas clair dans ce que je voulais. J'ai ajouté un Edit sur l'OP. S'il vous plaît pouvez-vous jeter un coup d'oeil? Merci La chose est que je suis en train de perdre sur le jeu de résultats si je n'utilise pas le && qui est crucial dans ce cas – DevNovice

0

Voici quelque chose plus proche de votre sous-requête SQL:

var q = dtResult.AsEnumerable()         // from Table 
    .Where(r => new[] { "First", "Employee" }.Contains(r["W_Text"])) // where W_Text in ('First','Employee') 
    .GroupBy(r => (int)r["l_id"])         // group by l_id 
    .Where(g => g.Count() > 1)          // having count(l_id) > 1 
    .Select(g => g.Key);            // Select l_id 

et voici une version un peu plus efficace:

var words = new HashSet<string> { "First", "Employee" }; // optional HashSet instead of List for a bit faster .Contains 

int iId = dtResult.Columns.IndexOf("l_id"); 
int iText = dtResult.Columns.IndexOf("W_Text"); 

var iRows = dtResult.Rows.Cast<DataRow>(); // a bit faster than dtResult.AsEnumerable() 

var results = new HashSet<int>( // optional HashSet instead of List for faster .Contains 
     iRows 
     .Where(r => words.Contains(r[iText])) // filter the rows that contain the words 
     .ToLookup(r => (int)r[iId])    // group by l_id 
     .Where(g => g.Count() >= words.Count) // filter the groups that contain all words 
     .Select(g => g.Key)      // select l_id 
    ); 

var dtCopy = iRows.Where(r => results.Contains((int)r[iId])).CopyToDataTable(); // InvalidOperationException if no DataRows 

mais si toutes les données sont déjà dans un DataTable, alors vous pouvez simplement grouper par l_id et obtenir les groupes qui contiennent tous les mots:

string[] words = { "First", "Employee" }; 

int iId = dtResult.Columns.IndexOf("l_id"); 
int iText = dtResult.Columns.IndexOf("W_Text"); 

var iRows = dtResult.Rows.Cast<DataRow>(); 

var idGroups = iRows.ToLookup(r => (int)r[iId]); // group by id 

var result = idGroups.Where(g => !words.Except(g.Select(r => r[iText] as string)).Any()); 

var dtCopy = result.SelectMany(g => g).CopyToDataTable(); 
+0

Merci pour votre réponse. Je vous en suis reconnaissant. Laissez-moi essayer et revenir bientôt. p.s. J'ai édité le PO avec une solution qui fonctionne pour moi. Cependant, je vais essayer le vôtre et revenir aussi. Merci encore! – DevNovice