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

Dotnet Discussion :

Optimisation (LINQ ?)


Sujet :

Dotnet

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé Avatar de dacid
    Homme Profil pro
    Inscrit en
    Juin 2003
    Messages
    1 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 065
    Par défaut Optimisation (LINQ ?)
    Bonjour @ tous,

    Je fait une requête sur une grosse table SQL SERVER (320 000 enregistrements) avec pas mal de relations.
    Certes, la requête prend un peu de temps à exécuter, mais ce qui me chagrine, c'est que le le temps de chargement de mes objets est plus longue encore...

    J'aimerais savoir si vous voyez une faille dans ma façon de faire.
    En gros, j'ai une collection par type d'objet et pour chaque dépendances, je cherche dans la collection ad hoc s'il existe déjà. Si non, il l'ajoute.

    Exemple pour charger des articles :
    J'ai un objet tblArticle et un objet tblArticles qui contient principalement une collection (BindingList) de tblArticle.
    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    Public Class tblArticles
        <DataMember>
        Public items As BindingList(Of tblArticle)
     
        Public Sub GetAll()
            Dim sb As StringBuilder = New StringBuilder($"Debut({DateTime.Now:mm:ss}) ")
            Dim dr As IDataReader
            Dim Fabs As tblFabricants = New tblFabricants(_Env)
     
            dr = _Env.OpenDr("SELECT * FROM ...")
            items.Clear()
    	sb.Append($"SqlExec({DateTime.Now:mm:ss}) ")
            While dr.Read
                items.Add(New tblArticle(_Env) With {
                    .Id = dr("ID").ToString().Trim(),
                    .Desc = dr("DES").ToString().Trim,
                    .Fab = Fabs.GetById(New tblFabricant(_Env) With {.Id = dr("FABID").ToString()})
                    ...etc...
                    })
            End While
            dr.Close() : dr.Dispose() : dr = Nothing
    	sb.Append($"Fin({DateTime.Now:mm:ss}, {items.Count} elmts).")
    	msgErr = sb.ToString()
        End Sub
    End Class
     
    Dans ma classe tblFabricants :
        ''' <summary>Retourne l'élément correspondant de la collection. S'il ne le trouve pas, il l'ajoute à la collection.</summary>
        Public Function GetById(o As tblFabricant) As tblFabricant
            If (String.IsNullOrEmpty(o.Id)) Then Return Nothing
            Dim elmt As tblFabricant= items.Where(Function(s) s.Equals(o)).SingleOrDefault()
            If (elmt Is Nothing) Then
                    elmt = o
                    items.Add(elmt)
                End If
            End If
            Return elmt
        End Function
     
    Dans ma classe tblFabricant:
        Public Overrides Function Equals(obj As Object) As Boolean
            Return ((Not obj Is Nothing) AndAlso (TypeOf (obj) Is tblFabricant) AndAlso (CType(obj, tblFabricant).Id = Id))
        End Function
    J'ai simplifié le code, il y a plus de GetById que ça (7 en tout), mais le principe est là.
    Pour info, le log: Debut(15:49) SqlExec(15:53) Fin(16:18, 362113 elmts).

    Est ce que faire du LINQ sur des listes annexes dans chaque boucle est optimisé (sinon, comment faire ?) ?
    Est ce que le fait d'utiliser une BindingList pour faire une collection est optimisé (je l'ai fait pour éviter des casts) ?

    [EDIT]
    J'ai ôté tous mes GetById pour faire directement du New... et en gros, ça ne change pas grand chose: Debut(20:33) SqlExec(20:37) Fin(20:55, 362113 elmts).
    18 secondes au lieu de 25, c'est toujours ça de prit, mais 18 c'est toujours énorme et ça me casse pas mal d'automatismes...
    [/EDIT]

    Merci d'avance.

  2. #2
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Je pense que le soucis vient de ton IDataReader, ou plutôt de l'image que tu t'en fais. IDataReader est fait pour traiter de large volume de données, et ne mets donc pas toutes les données issues d'une requête en cache. Mais elle recupère ce dont elle a besoin au fur et à mesure.

    Dans la mesure où tu fais un traitement sur des données et que tu mets le résultat de ce traitement dans une collection, tu auras au final toutes les données au format traité. Aussi, dans ce cas, je ne passerai pas par un reader mais ferais directement une requête Linq (donc traduire ta requête SQL en linq) et faire un .ToList() à la fin de cette requête pour récupérer, en une seule fois, toutes les données issues de la requêtes. Ensuite, tu itères afin de traiter tes données brutes.

    De même, quand tu fais ta requête initiale, peut être qu'en incluant des jointures tu peux récupérer les éléments que tu récupères en faisant des appels à tes GetByID. Ainsi, potentiellement, tu pourrais récupérer toutes les données utiles en une seule requête , et ensuite gérer la mise en cache.

    Enfin, oui, utiliser une BindingList induit des pertes de performance. Une BindingList génère des événements dès lors qu'un objet est ajouté/supprimé/modifié. Si tu n'en a pas besoin, il vaut mieux partir sur une List (en précisant éventuellement une capacité à sa création, histoire d'éviter des allocations supplémentaires en cours de route). Et si tu en as besoin, mais que tu n'as pas besoin de ces événements lors du traitement de ta requête, tu peux passer d'abord par une List et ensuite générer une BindingList à partir de cette liste. Un petit test de perfomance à réaliser pour voir s'il y a un gain notable ou pas

  3. #3
    Membre éprouvé Avatar de dacid
    Homme Profil pro
    Inscrit en
    Juin 2003
    Messages
    1 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 065
    Par défaut
    Bonjour Dorinf,

    Merci pour ta rapidité.
    En gros, tu me dis de faire du LinqToSql ?
    J'ai entendu parler de ce concept mais ne l'ai jamais utilisé car il est très lié à entity, mais je ne l'utilise pas.
    Quels objet utiliser alors pour faire la connexion ?
    Aurais tu un bref exemple, STP ?

    Merci.

  4. #4
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par dacid Voir le message
    En gros, tu me dis de faire du LinqToSql ?
    Pas tout à fait. Je voulais juste te signaler que le benchmark que tu crois faire n'est pas celui que tu fais. Tu penses dans un premier temps mesurer le temps nécessaire pour ta requête SQL alors qu'en réalité, tu peux en exécuter bien d'autres lorsque tu récupères les infos via le DataReader (je ne parle donc pas de tes GetByID).

    L'avantage que je vois avec LinqToSql (ou LinqToEntities, plus puissant, mais plus lourd aussi), c'est de pouvoir réaliser des requêtes fortement typées qui sont validées à la compilation. Je n'ai pas d'exemple sous la main, mais pour pouvoir utiliser LinqToSql c'est relativement simple :
    • Créer un fichier .dbml ("Classes Linq to SQL" dans Visual Studio) ;
    • Ajouter des tables dans le fichier .dbml grâce à l'explorateur de serveur
    • Dans ton code, instancier un objet Context (le nom de la classe va dépendre du nom de ton dbml et est modifiable dans les propriétés) ;
    • Pour chaque table dans ton dbml, tu as une propriété correspondante dans ton objet Context, que tu peux utiliser dans tes requêtes Linq.


    C'est le principe dans les grandes lignes. Trouver un exemple complet ne devrait pas être très compliqué

    Après, il est toujours possible d'utiliser des requêtes classiques à côté, notamment lorsque les requêtes sont très compliquées ou que le besoin de performance est vital (LinqToSql, comme tout ORM a un impact sur les perfs. Il est faible pour des requêtes classiques comme des SELECT avec jointures).

  5. #5
    Membre éprouvé Avatar de dacid
    Homme Profil pro
    Inscrit en
    Juin 2003
    Messages
    1 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 065
    Par défaut
    Dorinf,

    J'ai un peu avancé depuis tes propos de ton post précédent.
    Ce n'est pas possible pour moi d'utiliser un DBML, j'ai un model déjà trop avancé.

    J'ai mis la suite ici vu que le sujet n'est plus vraiment le même :
    http://www.developpez.net/forums/d15...e/#post8696773

  6. #6
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Je pense qu'on a surtout besoin d'avoir plus d'information. Avoir la requête exacte que tu exécutes et d'avoir le code pour la construction complète de tes objets.

    J'ai l'impression que parce qu'on parle de LinqToSql, tu penses que c'est cela que tu dois utiliser. Ce n'est pas forcément le cas. Je n'ai fais que pointer un avantage (typage fort => vérification des requêtes à la compilation) et une comparaison avec un DataReader.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/11/2015, 15h55
  2. Optimisation requête XML - LinQ
    Par jeyGey dans le forum Linq
    Réponses: 2
    Dernier message: 25/09/2014, 16h57
  3. Optimisation d'utilisation LINQ to XML
    Par Invité dans le forum C#
    Réponses: 0
    Dernier message: 04/02/2013, 10h28
  4. Linq - question optimisation requête
    Par boby62423 dans le forum Linq
    Réponses: 2
    Dernier message: 07/04/2009, 18h19
  5. [langage] Optimiser la lecture d'un fichier
    Par And_the_problem_is dans le forum Langage
    Réponses: 2
    Dernier message: 11/06/2002, 10h24

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