2010-05-02 8 views
4

j'ai des objets qui mettent en œuvre cette interface:méthode d'appel immédiatement après la construction de l'objet dans LINQ requête

public interface IRow 
{ 
    void Fill(DataRow dr); 
} 

Habituellement, lorsque je choisis quelque chose de db, je vais:

public IEnumerable<IRow> SelectSomeRows 
{ 
    DataTable table = GetTableFromDatabase(); 
    foreach (DataRow dr in table.Rows) 
    { 
    IRow row = new MySQLRow(); // Disregard the MySQLRow type, it's not important 
    row.Fill(dr); 
    yield return row; 
    } 
} 

maintenant avec .Net 4, je voudrais utiliser AsParallel, et donc LINQ.

Je l'ai fait quelques tests sur, et il accélère les choses beaucoup (IRow.Fill utilise la réflexion, il est donc difficile sur le CPU)

Quoi qu'il en soit mon problème est, comment puis-je faire pour créer un LINQ requête, qui appelle Fills dans le cadre de la requête, de sorte qu'il est correctement parallélisé?

Pour tester les performances, j'ai créé un constructeur qui a pris le DataRow comme argument, mais j'aurais vraiment aimé l'éviter si cela était possible.

Avec le constructeur en place, il est évidemment assez simple:

public IEnumerable<IRow> SelectSomeRowsParallel 
{ 
    DataTable table = GetTableFromDatabase(); 
    return from DataRow dr in table.Rows.AsParallel() 
     select new MySQLRow(dr); 
} 

Cependant, comme je l'ai dit, j'aimerais vraiment être en mesure de simplement bourrer ma méthode de remplissage dans la requête LINQ, et donc pas besoin la surcharge du constructeur.

Répondre

4

Vous devez faire une expression lambda multi-instruction, comme ceci:

table.AsEnumerable().AsParallel().Select(dr => 
    IRow row = new MySQLRow(); 
    row.Fill(dr); 
    return row; 
}); 
+0

Vous étiez en premier, donc vous obtenez le "bon" drapeau :-) Merci pour les nombreuses réponses, toujours agréable d'apprendre quelque chose. – Steffen

1

La réponse est heureusement très simple. Il suffit de le faire :) Il n'y a rien qui vous empêche d'appeler simplement une méthode dans la partie sélection de la requête

public IEnumerable<IRow> SelectSomeRowsParallel 
     { 
      DataTable table = GetTableFromDatabase(); 
      return from DataRow dr in table.Rows.AsParallel() 
       select (row => 
         var mysqlRow = new MySQLRow() 
         mysqlRow.Fill(row); 
         return mysqlRow;) 
     } 

Je ne suis pas sûr que vous pouvez farcir le lambda là-dedans (depuis quelques années que j'ai eu la chance d'écrire LINQ) si vous ne pouvez pas l'affecter à un Func

Func<IRow,DataRow> getRow = 
        (row => 
        var mysqlRow = new MySQLRow() 
        mysqlRow.Fill(row); 
        return mysqlRow;) 

puis appeler dans votre clause select

+1

Avez-vous essayé de compiler ceci, ou du moins de le taper dans l'éditeur C# qui détecte les erreurs de syntaxe? –

+0

@Thomas Nope c'est plutôt difficile sur mon téléphone mais thx pour les heads-up. J'ai essayé de compiler dans ma tête et j'en ai trouvé quelques-uns. Rien n'affecte le point de la réponse mais le code est plus compilateur au moins :) –

1

Je ne pense pas qu'il y ait un moyen de placer une opération impératif (par exemple un appel à la méthode Fill qui renvoie void) dans la syntaxe de requête LINQ, mais vous pouvez faire la même chose en utilisant l'appel explicite à la méthode Select, qui vous permet d'utiliser un code arbitraire:

DataTable table = GetTableFromDatabase(); 
return table.Rows.Cast<DataRow>().AsParallel().Select(dr => { 
    IRow row = new MySQLRow(); 
    row.Fill(dr); 
    return dr; }); 

Vous devez ajouter l'appel à Cast (parce que DataSets ne mettent pas en œuvre version générique de IEnumerable) et le reste du code est assez simple. Votre requête originale traduirait exactement à ces appels.

Si vous vouliez faire quelques astuces, vous pourriez modifier l'interface, de sorte que la méthode Fill renvoie quelque chose (par exemple int). Vous pouvez ensuite utiliser la clause let et ignorer la valeur renvoyée.

return from DataRow dr in table.AsParallel() 
     let IRow row = new MySQLRow() 
     let _ = row.Fill(dr) // ignoring return value; '_' is just variable name 
     select row; 

Il est possible d'utiliser ces méthodes d'appel de truc qui renvoient quelque chose, mais pas des méthodes qui renvoient void.