IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

Gros problème de performance


Sujet :

Windows Presentation Foundation

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut Gros problème de performance
    Bonjour à tous,

    Je développe une application en WPF et je rencontre un gros problème de performance pour le chargement des données. (environ 2 min).

    J'ai essayé d'appliquer le design pattern MVVM mais vu le résultat que j'obtiens, j'ai un doute sur ce que j'ai fait. Je vous explique ma démarche.


    J'ai un ViewModel que je vais appeller ObjetViewModel avec beaucoup de propriétés (> 50)
    J'ai un autre ViewModel que je vais appeler AllObjetViewModel et qui contient une observableCollection de ObjetViewModel (ListObjetViewModel).



    Dans AllObjetViewModel, je récupère la liste des objets de la base par linq
    et pour chaque objet récupéré je créé un ObjetViewModel que j'ajoute à ListObjetViewModel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Exemple :
    public AllObjetViewModel()
    {
        var req = from ...
        List temp = req.toList();
        foreach(objet o in temp)
         {
               ListObjetViewModel.add(new ObjetViewModel(o));
         }
    }
    Et après je mets l'observableCollection en itemsResource de la listView.


    Donc je voulais savoir si mon raisonnement était correct car j'ai de gros problème de performance pour le chargement des données.

  2. #2
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Ta requête linq est déjà enumerable, tu n'as pas besoin de la transformer en list pour itérer dessus normalement.
    On peut avoir plus d'info sur la requete linq d'ailleurs ?
    Moi, j'ai récemment eu le même problème que toi, et il se trouve que c'était du au piètre performance de linq justement.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    Merci d'avoir répondu.
    Ma requête est toute basique.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
                    var req = from c in dc.LigneCommande
                              orderby c.Projet
                              where c.Solde == false
                              select c;
    J'avais essayé les types anonymes mais j'avais pas vu d'amélioration.

    Ensuite dans mon ViewModel, j'initialise les propriétés en utilisant les relations des objets.
    Par exemple dans le constructeur du ViewModel je fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    NomClient = LigneCommande.Client.Nom;

  4. #4
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Tu utiles Linq to sql et charges depuis une BDD, ou LigneCommande est une collection d'objet que tu as toi-même créée ?
    Combien de lignes/éléments environ dans LigneCommande ?

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    Je charge de la BDD, dc c'est le nom de mon datacontext.
    La table LigneCommande est liée à 6 autres tables et je récupère environ 50 propriétés au final pour mon ViewModel.

    J'ai actuellement 2000 lignes dans la BDD pour les tests mais j'en aurais beaucoup plus.

  6. #6
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Ca ne devrait pas être aussi long, vu la simplicité.
    Compare le temps d'exécution de la requête, et le temps de création de tes vm.

    Fais un
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
                DateTime start = DateTime.Now;
     
                    var req = from c in dc.LigneCommande
                              orderby c.Projet
                              where c.Solde == false
                              select c;
     
                Console.WriteLine(req.Count());
     
                DateTime stop = DateTime.Now;
     
                Console.WriteLine((stop - start).TotalMilliseconds);

    Et tu devrais modifier comment tu ajoutes à ta liste
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ListObjetViewModel = new ObservableCollection<ObjetViewModel>(req.Select(o => new ObjetViewModel(o)));
    pour créer ta collection d'un coup, on ne sait jamais.

  7. #7
    Membre expert
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    2 210
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 2 210
    Points : 3 015
    Points
    3 015
    Par défaut
    Salut,

    LinqToSql est très opaque et à l'époque où je le testais (il y a deux ans ou plus), j'étais déçu par les performances : peut être que je ne m'y suis pas plongé suffisamment après...
    Du coup, j'étais repassé sur du ADO.Net.
    T'auras peut-être plus de précisions sur les performances de LinqToSql et d'autres pistes sur le forum Linq

    Sinon les problèmes de performances peuvent aussi se situer côté WPF. Lorsque tu affiches de nombreux enregistrements dans une liste, il faut s'assurer que celle-ci est en mode virtualisation.

  8. #8
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
                DateTime start = DateTime.Now;
     
                    var req = from c in dc.LigneCommande
                              orderby c.Projet
                              where c.Solde == false
                              select c;
     
                Console.WriteLine(req.Count()); // 698
     
                DateTime stop = DateTime.Now;
     
                Console.WriteLine((stop - start).TotalMilliseconds); // 31,2506
     
    ListObjetViewModel = new ObservableCollection<ObjetViewModel>(req.Select(o => new ObjetViewModel(o)));
     
                    DateTime stop2 = DateTime.Now;
                    Console.WriteLine((stop2 - stop).TotalMilliseconds); // 2640,6757
    C'est les résultats sur le serveur local. Sur le serveur de prod, les résultats sont encore plus long.

    Je virtualize bien la listview. J'avais déjà trouver cette astuce pour essayer d'améliorer les perfs.

    Donc je dois laisser tomber linq2sql pour avoir de meilleurs performances ? C'est dommage, c'était pas mal à utiliser.

  9. #9
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Non, ne laisse pas tomber linq2sql tout de suite, tu peux voir que ça vient de la création de tes ObjetViewModel.
    Que fait le constructeur de cette classe ?

  10. #10
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    J'initialise les propriétés du ViewModel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    public class ObjetViewModel: INotifyPropertyChanged
    {
                 //L'événement pour la mise à jour de la propriété
                  private static readonly PropertyChangedEventArgs ClientChangedEventArgs = new PropertyChangedEventArgs("Client");
     
           //le constructeur
           public ObjetViewModel(LigneCommande paramLigneCommande)
          {
                    if (paramLigneCommande.Client != null)
                         Client = paramLigneCommande.Client;
                    if (paramLigneCommande.Projet.HasValue)
                         Projet = paramLigneCommande.Projet;
                    if (paramLigneCommande.Solde.HasValue)
                         Solde = paramLigneCommande.Solde;
                    ....
           }
     
            //la propriété
            private String _Client;
            public String Client 
            {
                get
                {
                    return _Client;
                }
                set
                {
                    _Client = value;
                    RaisePropertyChanged(ClientChangedEventArgs);
                }
            }
    }

  11. #11
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Je ne vois pas grand chose d'autre que de setter directement tes membres privé, plutôt que tes propriétés.
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                    if (paramLigneCommande.Client != null)
                         _Client = paramLigneCommande.Client;

    Sinon, tu n'utilises que des champs de ta table LigneCommande ? Pas de jointure sur une autre table par exemple ?

  12. #12
    Membre expérimenté Avatar de davcha
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 258
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 258
    Points : 1 539
    Points
    1 539
    Par défaut
    Tu peux t'arranger pour améliorer un peu les perfs de ton constructeur, comme ça :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Objet
    {
    	public string Propriete1 { get; set; }
    	public string Propriete2 { get; set; }
    }
    class ObjetViewModel
    {
    	Objet _objet;
    	bool _propriete1HasNewValue;
    	string _propriete1;
    	bool _propriete2HasNewValue;
    	string _propriete2;
     
    	public ObjetViewModel(Objet objet)
    	{
    		_objet = objet;
    	}
     
    	public string Propriete1
    	{
    		get { return _propriete1HasNewValue ? _propriete1 : _objet.Propriete1; }
    		set { _propriete1 = value; _propriete1HasNewValue = true; }
    	}
    	public string Propriete2
    	{
    		get { return _propriete2HasNewValue ? _propriete2 : _objet.Propriete2; }
    		set { _propriete2 = value; _propriete2HasNewValue = true; }
    	}
    }

  13. #13
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    Oui j'en ai aussi

    Par exemple entre la table LigneCommande et ApprosBE, j'ai la table AssocLigneCommandeApprosBE (table d'association).

    Ce que je fais, c'est que je récupère le dernier élément modifié (pas ajouté) et je l'initialise dans le viewModel

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
      if (paramLigneCommande.getApprosBE() != null && paramLigneCommande.getApprosBE().NomApproPrevuValide.HasValue)
              _NomPrevuValide = paramLigneCommande.getApprosBE().NomApproPrevuValide.Value;
      if (paramLigneCommande.getApprosBE() != null && paramLigneCommande.getApprosBE().NomApproPrevuValide.HasValue)
               _NomPrevuValide = paramLigneCommande.getApprosBE().NomApproPrevuValide.Value;
    ...
    Dans la classe LigneCommande la méthode getApprosBE me ramène le dernier élément modifié.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
            public ApprosBE getApprosBE()
            {
                ApprosBE appros = new ApprosBE();
                if (this.AssocLigneCommandeApprosBE != null && this.AssocLigneCommandeApprosBE.Count != 0)
                {
                    AssocLigneCommandeApprosBE b = this.AssocLigneCommandeApprosBE[0];
                    foreach (AssocLigneCommandeApprosBE a in this.AssocLigneCommandeApprosBE)
                    {
                        if (b.DateModif >= a.DateModif)
                            appros = b.ApprosBE;
                        else
                        {
                            b = a;
                            appros = a.ApprosBE;
                        }
                    }
                }
                return appros;
            }

  14. #14
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Testes ton code en sautant l'appel à des jointures, met un "return null" à la première ligne de ta fonction getApprosBE pour voir si ça vient de là.
    Dans mon cas, c'était les jointures qui merdaient, et il m'a suffit de rechercher moi-même les éléments joints, en faisant une requête linq qu'il compile correctement directement en requête sql.

    Edit: Et pouquoi n'as-tu pas utiliser linq pour trouver ta ligne ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ApprosBE appros = (from b in this.AssocLigneCommandeApprosBE
                orderby b.DateModif
                select b).Take(1).FirstOrDefault();
    return appros;

  15. #15
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 17
    Points : 7
    Points
    7
    Par défaut
    J'ai essayé en mettant un return null dans les méthodes de jointure et je gagne quand même la moitié du temps. Ca représente aussi 40 propriétés en moins.

    Avant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
                DateTime start = DateTime.Now;
     
                    var req = from c in dc.LigneCommande
                              orderby c.Projet
                              where c.Solde == false
                              select c;
     
                Console.WriteLine(req.Count()); // 698
     
                DateTime stop = DateTime.Now;
     
                Console.WriteLine((stop - start).TotalMilliseconds); // 31,2506
     
    ListObjetViewModel = new ObservableCollection<ObjetViewModel>(req.Select(o => new ObjetViewModel(o)));
     
                    DateTime stop2 = DateTime.Now;
                    Console.WriteLine((stop2 - stop).TotalMilliseconds); // 2640,6757
    Après
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
                DateTime start = DateTime.Now;
     
                    var req = from c in dc.LigneCommande
                              orderby c.Projet
                              where c.Solde == false
                              select c;
     
                Console.WriteLine(req.Count()); // 698
     
                DateTime stop = DateTime.Now;
     
                Console.WriteLine((stop - start).TotalMilliseconds); // 15,6248
     
    ListObjetViewModel = new ObservableCollection<ObjetViewModel>(req.Select(o => new ObjetViewModel(o)));
     
                    DateTime stop2 = DateTime.Now;
                    Console.WriteLine((stop2 - stop).TotalMilliseconds); // 1265,6088
    Edit: Et pouquoi n'as-tu pas utiliser linq pour trouver ta ligne ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ApprosBE appros = (from b in this.AssocLigneCommandeApprosBE
                orderby b.DateModif
                select b).Take(1).FirstOrDefault();
    return appros;
    Je ne maîtrise pas encore très bien Linq et même avec la requête il n'y a pas de différence.

  16. #16
    Membre régulier
    Inscrit en
    Octobre 2005
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 62
    Points : 85
    Points
    85
    Par défaut
    Si tu veux utiliser linq2sql, tu est obligé d'utiliser linq presque tout le temps.
    Si tu cherches, par code, la ligne désirée, tu vas charger la table complète à chaque fois.
    Tu dois utiliser linq pour qu'il puisse compiler ça en requête sql, sinon, adieu les performances. Et même en utilisant linq, il faut vérifier qu'il la compile bien et la bidouiller si il a du mal, sinon, comme toi, il va charger toute la table et exécuter la requête sur cette copie locale.

Discussions similaires

  1. Gros problème de performance
    Par Syl_20 dans le forum OpenGL
    Réponses: 15
    Dernier message: 16/12/2007, 18h19
  2. GROS problèmes de performances
    Par fda dans le forum MS SQL Server
    Réponses: 6
    Dernier message: 02/08/2007, 14h02
  3. [Tomcat] Gros problèmes de performance
    Par Tourix dans le forum Tomcat et TomEE
    Réponses: 1
    Dernier message: 04/06/2007, 15h58
  4. [XNA] GROS problèmes de performances
    Par kawash dans le forum XNA/Monogame
    Réponses: 12
    Dernier message: 20/02/2007, 23h03
  5. Gros problème de performance
    Par steelidol dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 23/11/2006, 08h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo