2009-11-19 4 views
0

Ce n'est pas vraiment un problème, mais plus d'une préoccupation que j'apprécierais une contribution s'il vous plaît. Winforms C# .net3.5 [sp1] Visual Studio 2008 en utilisant Linq2Sql (plus précisément PLINQO ... ce qui est fantastique btw!)Performance concernant le type de retour pour la requête LINQ compatible avec le tri automatique

J'ai un resultset retournant +/- 19000 lignes de données (avec environ 80 octets de données par ligne) et a opté pour pousser la méthode de récupération de données à l'arrière-plan et mettre à jour l'interface utilisateur en conséquence. Cela fonctionne bien.

Cependant, j'ai remarqué quelques différences de performances lors de l'utilisation de différents types de retour pour ma méthode de récupération de données Ling. Je sais que tout le monde suggère de renvoyer un List<T> ou IEnumarable<T>, et de définir la source de données de DataGridView à cela, mais malheureusement, il ne supporte pas le tri natif pour les objets. Après avoir creusé autour j'ai trouvé le SortableBindingList<T> sur MSDN here. Je l'ai appliqué, et la Grid a pris moins d'une seconde pour se peupler - cependant quand j'ai cliqué sur une colonne pour la trier, il a fallu un peu plus d'une seconde pour mettre en place le tri.

J'ai ensuite décidé d'aller sur la route DataTable, mais j'ai découvert que la méthode ToDataTable a été supprimée, mais après plus de creuser trouvé un moyen de l'implémenter sur ce MSDN article. Après l'avoir appliqué, j'ai découvert que la récupération prenait environ 2 secondes pour peupler la grille, mais par la suite le tri (sur 19000 lignes!) Était instantané !! Naturellement je suis resté avec cette approche.

Gardez également à l'esprit que j'ai désactivé toute modification/ajout/suppression dans la grille. La grille est purement pour l'affichage des données. Toutes les autres opérations CRUD sont fournies par des formulaires de dialogue en fonction de la ligne sélectionnée en cours (colonne de clé primaire cachée).

Voici le code que j'utilisé pour les deux méthodes:

1) SortableBindingList

//declare private member 
    private SortableBindingList<PdtAllocation> listAlloc = null; 

    private void RefreshData() { 
     bcsDataContext ctx = new bcsDataContext(); 
     try { 
      listAlloc = new SortableBindingList<PdtAllocation>(ctx.PdtAllocation.ToList()); 
     } 
     catch (Exception) { 
      throw; 
     } 
     finally { 
      ctx.Dispose(); 
     } 

     dataGridView1.DataSource = listAlloc; 
    } 

2) CopyToDataTable

//declare private member 
    private DataTable dt = null; 

    private void RefreshData() { 
     dt = new DataTable(); 
     bcsDataContext ctx = new bcsDataContext(); 
     try { 
      ctx.PdtAllocation.CopyToDataTable(dt, LoadOption.PreserveChanges); 
     } 
     catch (Exception) { 
      throw; 
     } 
     finally { 
      ctx.Dispose(); 
     } 

     dataGridView1.DataSource = dt; 
    } 

Maintenant, je sais que cela semble probablement comme un "demandé-et-répondu" mais j'apprécierais vraiment votre entrée, aussi bien que tous les problèmes connus avec aller la route CopyToDataTable().

Merci ... et excuses pour la requête looong!

Répondre

1

Voici ma réponse à votre question (en passant par la route SortableBindingList). Avez-vous utilisé un algorithme générique de tri basé sur la réflexion? Je l'ai fait au début et la performance était vraiment mauvaise. Finalement, j'ai trouvé la solution suivante: fournir le tri par défaut dans SortableBindingList<T> mais aussi laisser la possibilité d'implémenter un tri spécialisé dans les classes dérivées.

Voici du code.

En SortableBindingList<T>.ApplySortCore():

protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) 
{ 
    // Check if the sorted property implements IComparable 
    ... 

    List<T> itemsList = (List<T>)this.Items; 

    Comparison<T> comparer = GetComparer(prop); 

    itemsList.Sort(comparer); 

    if (direction == ListSortDirection.Descending) 
    { 
     itemsList.Reverse(); 
    } 

    ... 
    // Set sort state (is sorted, sort property, sort dir) 
} 

Le SortableBindingList<T> générique fournit une base, trieuse à base de réflexion:

protected virtual Comparison<T> GetComparer(PropertyDescriptor prop) 
{ 
    return new Comparison<T>(delegate(T x, T y) 
    { 
     if (prop.GetValue(x) != null) 
      return ((IComparable)prop.GetValue(x)).CompareTo(prop.GetValue(y)); 
     else if (prop.GetValue(y) != null) 
      return -1 * ((IComparable)prop.GetValue(y)).CompareTo(prop.GetValue(x)); 
     else 
      return 0; 
    }); 
} 

Comme vous pouvez le voir, GetComparer() est virtuel, donc on peut le remplacer dans une classe dérivée de SortableBindingList<T> afin de fournir un beaucoup rapidement, ajusté au type de la propriété étant effectivement triée. Par exemple, alors que le comparateur générique a trié (par une propriété String) 10000 enregistrements en 4 secondes, le comparateur spécialisé a fait le même travail en 70ms.

class CustomerList : SortableBindingList<Customer> 
{ 
    protected override Comparison<Customer> GetComparer(PropertyDescriptor prop) 
    { 
     Comparison<Customer> comparer = null; 
     switch (prop.Name) 
     { 
      case "FirstName": 
       comparer = new Comparison<Customer>(delegate(Customer x, Customer y) 
       { 
        string xx = (null == x) ? null : x.FirstName; 
        string yy = (null == y) ? null : y.FirstName; 
        return String.Compare(xx, yy); 
       }); 
       break; 
      ... 
     } 
     return comparer; 
    } 
} 

Une note finale: une grande partie du code affiché a été copié/inspiré d'autres sources, comme SO ou Microsoft, de sorte que le crédit est pas à moi. Ma seule contribution était de virtualiser le comparateur mais je suis sûr qu'un peu de googling ferait mieux surface, des solutions antérieures basées sur la même idée.

+0

Salut Sorin! "* Avez-vous utilisé un algorithme de tri générique basé sur la réflexion? *" Non, juste utilisé comme tel ... n'a pas changé une chose. Le code fourni par vous est génial! mais aucune raison pour laquelle je ne devrais pas utiliser DataTable ?? – Shalan

+0

Eh bien, ma première réaction a été "pourquoi copier dans un DataTable quand on pourrait trier la collection à portée de main?". Je ne sais pas. Peut-être que copier vers un DataTable peut donner d'autres options pratiques que je ne peux pas voir maintenant. Dans l'attente de voir ce que les autres ont à dire à ce sujet. –

+0

Sur son propre "ctx.PdtAllocation" renvoie System.Data.Linq.Table . Par la suite, le choix de «convertir» à un autre type reste. Ma compréhension est que la quantité de travail prise pour ".CopyToDataTable()" est la même que ".ToList()" ... alors je suis allé avec l'option qui a fonctionné mieux (sur la surface) à la fin. D'autres pensées à ce sujet? – Shalan

Questions connexes