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

Linq Discussion :

[C#][Linq]Différence de procédure


Sujet :

Linq

  1. #1
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut [C#][Linq]Différence de procédure
    Bonjour,

    Je sais qu'il y a des "pointures" linq sur ce forum. Quelqu'un peut-il donc me dire pourquoi cette méthode fonctionne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                var clients =
                    (from c in formMDI.Bdd.Client
                     select c).ToList();
     
                var listeClients =                                      // rechercher tous les clients pas encore utilisés
                    (from c in clients
                     where !c.EstDansLeTableau(tabl1)
                     && !c.EstDansLeTableau(tabl2)
                     orderby c.NumClient
                     select c).ToList();
    Mais pas celle-ci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
                var listeClients2 =
                    (from c in formMDI.Bdd.Client
                     where !c.EstDansLeTableau(tabl1)
                     && !c.EstDansLeTableau(tabl2)
                     orderby c.NumClient
                     select c).ToList();
    dans le second cas, j'obtiens une erreur :

    LINQ to Entities ne reconnaît pas la méthode « Boolean EstDansLeTableau(FactuLite.Client, System.String[]) », et cette dernière ne peut pas être traduite en expression de magasin.
    Y a-t-il une solution pour éviter de créer une liste avec tous les éléments de la table avant de faire la recherche?

    Pour information, voici la méthode "EstDansLeTableau", qui est toute bête :

    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 static bool EstDansLeTableau(this Client client, string[] tableau)
            {
                bool result = false;                        // valeur de retour
                if (tableau != null)
                {
                    long NumTableau;                            // un numéro du tableau
                    long numClient = client.NumClient;          // récupérer numéro du client
     
                    foreach (var item in tableau)
                    {
                        long.TryParse(item, out NumTableau);    // tenter la conversion de l'élément en long
                        if (numClient == NumTableau)            // si ce numéro est celui du client
                        {
                            result = true;                      // on a trouvé
                            break;                              // et fin de la recherche
                        }
                    }
                }
                return result;
            }
    Merci d'avance

    Claude

  2. #2
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Par défaut
    est de quelle nature ?

    Si c'est une entité Linq (mappée à une table) alors je pense que la réponse est la suivante:

    Dans le premier cas tu fais une requête qui va être traduite en SQL par Linq to Entities. Puis tu fais un ToList qui va executer cette requête et ramener des objets.
    Puis tu utilises EstDansLeTableau sur le résultat de la requête précédente, et là tu travailles sur des objets en mémoire: Linq to Object.


    Dans le deuxième cas tu essayes de tout faire d'un coup. Linq to Entities essaye donc de traduire la requête en SQL. Et il plante parcequ'il ne sait pas traduire ta fonction EstDansLeTableau en SQL.
    Les règles du forum
    Le trio magique : FAQ + Cours + fonction rechercher
    Mes articles
    Pas de questions par messages privés svp

    Software is never finished, only abandoned.

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    - 1ere méthode : tu effectues la 1ere requête sur la base de données et tu transformes le résultat en List<Client>. La 2e requête Linq ne travaille donc plus sur un IQueryable<Client> mais sur un IEnumerable<Client>, donc la requête n'est pas transformée en SQL pour être exécutée sur la base, et l'appel à EstDansLeTableau se passe bien

    - 2e méthode : tu travailles directement sur un IQueryable, sans conversion intermédiaire en IEnumerable, donc Linq essaie de transformer ta requête en SQL, mais comme la fonction EstDansLeTableau n'existe pas en SQL, ça plante...

  4. #4
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Par défaut
    A part tes mots savants, c'est ce que j'ai dit
    Les règles du forum
    Le trio magique : FAQ + Cours + fonction rechercher
    Mes articles
    Pas de questions par messages privés svp

    Software is never finished, only abandoned.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par The_badger_man Voir le message
    A part tes mots savants, c'est ce que j'ai dit

    en fait j'avais pas vu ton message quand j'ai posté...

  6. #6
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    Bonjour,

    Merci, les deux explications sont limpides.

    Si j'ai bien compris les implications :

    1) Il m'est impossible d'intégrer ce genre de fonction dans une requête linq portant sur une table sqLite

    2) La seule façon de procéder, si je veux éviter de rapatrier l'intégralité de la table en RAM serait donc de parcourir la table et de faire le tri dans un foreach, au lieu d'utiliser ToList. Si j'ai tout saisi, c'est justement ce que fait une requête suivie d'un ToList : une sélection au moment du parcours des éléments de la table :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     
    var query =
        from c in formMDI.Bdd.Client.Include("Adresse")
        orderby c.NumClient
        select c;
     
    var listeClients = new List<Client>();          // créer une liste de clients
    foreach (var client in query)                   // parcourir la table
        if (!client.EstDansLeTableau(tabl1))        // si le client n'est utilisé dans aucune des zones texte in et out
            if (!client.EstDansLeTableau(tabl2))
                listeClients.Add(client);           // l'ajouter dans la liste des clients à afficher

    C'est bien ça, où il y a une méthode plus simple?

    J'allais dire que ce n'était pas dérangeant pour sqLite qui travaille en local, mais que ramener tous les éléments à partir d'un serveur était lourd, quand je me suis rappelé que sqLite ne permet pas de stocker des procédures, mais sql serveur si. Serait-ce la raison de cette différence?

    Merci
    Claude

    --------------

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Ben fait s'il y a des critères qui te permettent de pré-filtrer les données directement en SQL, tu peux les utiliser, faire le ToList sur le résultat, et ensuite filtrer selon ta fonction EstDansLeTableau.

    Par contre, j'ai une info qui devrait te plaire

    Avec SQLite, tu peux définir des fonctions en C# (ou en C, ou n'importe quel langage à partir duquel tu utilises SQLite) et les "mapper" avec un nom de fonction utilisable en SQL. J'avais testé ça il y a quelque temps, j'essaierai de te retrouver le code.

    Par contre je suis pas sûr que le provider EF de SQLite soit capable de réaliser que tu as mappé la méthode C# avec une fonction SQL... en fait, en y réfléchissant un peu ça parait même peu probable

  8. #8
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    Ben fait s'il y a des critères qui te permettent de pré-filtrer les données directement en SQL, tu peux les utiliser, faire le ToList sur le résultat, et ensuite filtrer selon ta fonction EstDansLeTableau.
    J'ai fait comme ça, sauf qu'au lieu de faire un tolist je fais un foreach et je trie dans la boucle. Ca m'évite de rappatrier le "pré-résultat" en Ram, on ne sait jamais jusqu'où grandira la base dans le futur.

    Par contre, j'ai une info qui devrait te plaire

    Avec SQLite, tu peux définir des fonctions en C# (ou en C, ou n'importe quel langage à partir duquel tu utilises SQLite) et les "mapper" avec un nom de fonction utilisable en SQL. J'avais testé ça il y a quelque temps, j'essaierai de te retrouver le code.
    Ca c'est super. Je pensais que ça ne fonctionnait pas avec SQLite mais seulement avec Sql server. Ca m'intéresse fortement

    Par contre je suis pas sûr que le provider EF de SQLite soit capable de réaliser que tu as mappé la méthode C# avec une fonction SQL... en fait, en y réfléchissant un peu ça parait même peu probable
    Dommage. Ca obligerait donc à accéder à ces fonctions en bas niveau, sans pouvoir utiliser linq to entities, mais on ne peut pas tout avoir.

    Mais bon, dans l'attente mon tri fonctionne, je ne suis pas bloqué, j'attends tes infos et dans l'attente merci de ces informations.

    Claude

  9. #9
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Par défaut
    J'ai fait comme ça, sauf qu'au lieu de faire un tolist je fais un foreach et je trie dans la boucle. Ca m'évite de rappatrier le "pré-résultat" en Ram, on ne sait jamais jusqu'où grandira la base dans le futur.
    T'as pas compris. Lorsque tu fais le foreach, la requête SQL est executée. Tu travailles alors sur des objets en mémoires.
    Avec Linq to SQL tu travailles toujours sur des objets, pas sur la base directement.
    Les règles du forum
    Le trio magique : FAQ + Cours + fonction rechercher
    Mes articles
    Pas de questions par messages privés svp

    Software is never finished, only abandoned.

  10. #10
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    T'as pas compris. Lorsque tu fais le foreach, la requête SQL est executée. Tu travailles alors sur des objets en mémoires.Avec Linq to SQL tu travailles toujours sur des objets, pas sur la base directement.
    Voici comment moi j'avais compris le fonctionnement (je travaille en linq to entities) :

    - Lorsque je fais une requête, je n'exécute rien sur le moment, j'envoie mes conditions de requête au moteur de la BDD

    - La requête est exécutée soit lorsque je parcours la table, soit lorsque je fais un "tolist" ou toute autre opération équivalente.

    - Lorsque je parcours des éléments avec un foreach, les éléments sont rapatriés un par un à partir de la table avec un indexage style yield return.

    Et donc si je fais un foreach avec un test qui permet d'ajouter l'élément à une liste en fonction du résultat, je n'ai en mémoire que la liste résultat.

    Par contre, si je fais un tolist puis une requête sur les objets ramenés par le tolist, j'ai en mémoire la liste de tous les éléments de ma table renvoyés par la requête, ce qui risque d'être beaucoup plus conséquent.

    Autrement dit, ma vision de la chose était celle-ci :

    Requête linq sur la table : non exécutée
    foreach( On ramène un objet du résultat de la requête à partir de la table)
    tester l'objet
    si pertinent , ajouter au résultat final dans une liste par exemple.

    Par contre, avec tolist

    - Requête sur la table linq.tolist : requête envoyée, exécutée, et rappatriement de tous les objets correspondants à la requete dans une liste

    - Requête linq.tolist sur l'objet avec mes méthodes :
    Création d'une nouvelle liste (ou la même) avec le résultat filtré

    Bref, les deux méthodes n'occupent pas la même place en Ram.

    Par contre, tu me dis, toi, si je suis bien, que lorsque je fais mon foreach, en réalité j'exécute la requête, je rapatrie tout le résultat sur un objet "virtuel" qui serait une liste? ou autre chose? et qu'en parcourant les éléments dans le foreach je ne parcourrais pas les éléments de la table sqlite, mais les éléments de mon objet "non nommé" créé implicitement lors du premier passage dans la boucle "foreach".

    Bref, quelque-chose comme ceci :

    - Linq sur la table : non exécuté
    foreach : création d'un "tolist" ou autre chose d'équivalent de façon implicite
    parcours des objets ramenés en ram

    Donc, avec ta vision des choses, faire un "tolist" ou un "foreach" produit le même résultat : ramener tous les éléments en Ram (objets) et manipuler tout dans la ram.

    C'est bien ça?
    J'avoue que ce n'est pas comme ça que j'avais compris le mécanisme, je pensais les éléments ramenés un par un de la table, sauf indication contraire genre "tolist".

    Et si j'ai besoin d'un seul élément, genre

    foreach element in base.table
    si élément == élément.maméthode()
    enregistrer élément et break;

    Ca veut dire qu'en fait je ramène toute la table dans la ram juste pour un élément? C'est complètement improductif et annule tout intérêt de linq sur des tables.

    Je pensais que c'était traduit en :

    ramène un élément de la table
    si élément ==
    etc.

    Si j'ai mal compris, alors comment fait-on pour ne ramener qu'un élément à la fois, si la table ne tient pas en intégralité dans la Ram? Je dois faire une boucle de recherche sur index dans la table?

    Claude

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par The_badger_man Voir le message
    T'as pas compris. Lorsque tu fais le foreach, la requête SQL est executée. Tu travailles alors sur des objets en mémoires.
    Je ne crois pas, non. Je pense comme ClaudeBg que le foreach récupère un seul objet à chaque itération.. Pour moi, le fait d'énumérer la collection exécute la requête (probablement un ExecuteReader en interne), et fait un Read sur le reader obtenu à chaque itération

    Mais je peux me tromper...

  12. #12
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Par défaut
    Citation Envoyé par ClaudeBg Voir le message
    - Lorsque je fais une requête, je n'exécute rien sur le moment, j'envoie mes conditions de requête au moteur de la BDD
    Rien n'est effectué effectivement. Mais rien n'est envoyé au moteur de la BDD. C'est fait lorsque tu parcours la variable contenant le requête (tolist, foreach, etc).

    Citation Envoyé par ClaudeBg Voir le message
    - Lorsque je parcours des éléments avec un foreach, les éléments sont rapatriés un par un à partir de la table avec un indexage style yield return.
    non. Une seule requête est effectuée.


    Citation Envoyé par ClaudeBg Voir le message
    Et donc si je fais un foreach avec un test qui permet d'ajouter l'élément à une liste en fonction du résultat, je n'ai en mémoire que la liste résultat.
    Et les objets qui ne satisfont pas le test, ils sont où ? Il sont bien en mémoire. Il a bien fallu les charger pour que tu fasse ton test.

    Citation Envoyé par ClaudeBg Voir le message
    Par contre, si je fais un tolist puis une requête sur les objets ramenés par le tolist, j'ai en mémoire la liste de tous les éléments de ma table renvoyés par la requête, ce qui risque d'être beaucoup plus conséquent.
    Effectivement, ça peut être lourd.

    Citation Envoyé par ClaudeBg Voir le message
    Autrement dit, ma vision de la chose était celle-ci :

    Requête linq sur la table : non exécutée
    foreach( On ramène un objet du résultat de la requête à partir de la table)
    tester l'objet
    si pertinent , ajouter au résultat final dans une liste par exemple.
    Si t'as table contient 100 lignes ça veut dire que tu vas faire 100 requêtes à la suite pour récupérer un seul objet à la fois ?
    Une seule requête ramenant les 100 lignes d'un coup est plus optimisée.

    Citation Envoyé par ClaudeBg Voir le message
    Bref, les deux méthodes n'occupent pas la même place en Ram.
    Si ta technique fonctionnait, ça reviendrait au même en ram. Tu aurais au final autant d'objets d'un coté comme de l'autre. Charger 100 lignes une a une contre charger 100 lignes d'un coup, ça fait 100 objets à la fin.

    Citation Envoyé par ClaudeBg Voir le message
    Par contre, tu me dis, toi, si je suis bien, que lorsque je fais mon foreach, en réalité j'exécute la requête, je rapatrie tout le résultat sur un objet "virtuel" qui serait une liste? ou autre chose? et qu'en parcourant les éléments dans le foreach je ne parcourrais pas les éléments de la table sqlite, mais les éléments de mon objet "non nommé" créé implicitement lors du premier passage dans la boucle "foreach".
    oui. Les éléments dont tu parles sont des objets, tu ne manipules pas ta table directement.
    Requête linq --> traduction en sql (je simplifie) --> execution de la requête sur la base --> récupération du résultat --> instanciation des objets correspondants --> travail sur ces objets (utilisation de ta méthode de test).
    Le but c'est de travailler avec des objets et de ne pas toucher la base.

    Tu n'as qu'à regarder les requêtes générées pour vérifier.


    Citation Envoyé par ClaudeBg Voir le message
    Et si j'ai besoin d'un seul élément, genre

    foreach element in base.table
    si élément == élément.maméthode()
    enregistrer élément et break;

    Ca veut dire qu'en fait je ramène toute la table dans la ram juste pour un élément? C'est complètement improductif et annule tout intérêt de linq sur des tables.
    Pourquoi "pour juste un élément" ? Là tu ne travailles pas sur un seul élément étant donné que tu fais un foreach. Tu veux donc travailler sur TOUS les éléments de base.table.
    Tu dis que tu veux tous les éléments de base.table, linq te les ramène...


    Citation Envoyé par ClaudeBg Voir le message
    Si j'ai mal compris, alors comment fait-on pour ne ramener qu'un élément à la fois, si la table ne tient pas en intégralité dans la Ram? Je dois faire une boucle de recherche sur index dans la table?
    Le truc c'est que dans ton cas tu n'as pas besoin de te poser la question.
    Tu veux récupérer tous les clients donc les NumClient ne sont pas contenus dans tabls1 et tabl2. C'est bien ça ?
    Cette requêtes est tout à fait traductible en SQL. Ta méthode EstDansLeTableau ne l'ait pas (comme on te l'a expliqué au début) mais la requête en elle même si. C'est un simple SELECT * FROM CLIENT WHERE NUMCLIENT NOT IN ('146', '1258', '8756').

    En Linq ça donne ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    var listeClients2 =
                    (from c in formMDI.Bdd.Client
                     where !tabl1.Contains(c.NumClient.ToString())
                     && !tabl2.Contains(c.NumClient.ToString())
                     orderby c.NumClient
                     select c).ToList();
    Cette requête sera donc entièrement traduite en SQL et tout (test des valeurs NumClient) se fera dans la base. Tu récupéreras directement la liste attendue.
    http://blog.wekeroad.com/blog/creati...h-linq-to-sql/

    PS: je n'ai pas testé le code
    PS2: je ne trouve pas très top de stocker des long dans un tableau de string
    Les règles du forum
    Le trio magique : FAQ + Cours + fonction rechercher
    Mes articles
    Pas de questions par messages privés svp

    Software is never finished, only abandoned.

  13. #13
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par The_badger_man Voir le message
    Et les objets qui ne satisfont pas le test, ils sont où ? Il sont bien en mémoire. Il a bien fallu les charger pour que tu fasse ton test.
    Oui, mais à mon avis un seul objet à la fois est généré... si un objet ne satisfait pas le test, on ne l'ajoute pas à la liste, donc à l'itération suivante il n'y a plus de référence dessus, et le GC peut le récupérer

    Citation Envoyé par The_badger_man Voir le message
    Si ta technique fonctionnait, ça reviendrait au même en ram. Tu aurais au final autant d'objets d'un coté comme de l'autre. Charger 100 lignes une a une contre charger 100 lignes d'un coup, ça fait 100 objets à la fin.
    Oui, mais ils ne sont pas tous présents en RAM en même temps... ce qui fait toute la différence si tu as une table de plusieurs millions d'éléments !

    Encore une fois, je peux me tromper, mais je pense que les objets entités sont instanciés au fur et à mesure que les résultats de la requête sont récupérés. Si tu fais un foreach sur une requête qui renvoie 1 million de lignes, il ne va pas créer 1 million d'objets avant de commencer à les renvoyer à l'énumérateur, sinon ce serait impossible de faire des traitements de masse à moins d'avoir une RAM énorme

  14. #14
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    Je n'ai pas les connaissances pour savoir comment ça se passe effectivement, mais par compte je peux répondre aux questions de façon logique

    Et les objets qui ne satisfont pas le test, ils sont où ? Il sont bien en mémoire. Il a bien fallu les charger pour que tu fasse to
    Pour ça, je pense exactement comme Tomlev, la référence de l'objet "importé" serait réaffectée à l'objet suivant, et donc un seul objet à la fois en Ram (plus la liste des résultats, évidemment).

    Si t'as table contient 100 lignes ça veut dire que tu vas faire 100 requêtes à la suite pour récupérer un seul objet à la fois ?
    Une seule requête ramenant les 100 lignes d'un coup est plus optimisée.
    Ben oui, mais ce serait justement l'avantage d'avoir les deux méthodes : 100 lignes? je fais un "tolist". 1 million de lignes à parcourir? je fais un foreach.

    Si ta technique fonctionnait, ça reviendrait au même en ram. Tu aurais au final autant d'objets d'un coté comme de l'autre. Charger 100 lignes une a une contre charger 100 lignes d'un coup, ça fait 100 objets à la fin.
    Je suis de nouveau du même avis que Tomlev, si j'ai 100.000 objets à parcourir et que mon test doit en ramener 100 : Avec le tolist j'ai 100.000 objets en Ram + les 100 du résultat. Avec le foreach j'aurais 1 objet + les 100 du résultat. Il n'y a pas photo.

    Pourquoi "pour juste un élément" ? Là tu ne travailles pas sur un seul élément étant donné que tu fais un foreach. Tu veux donc travailler sur TOUS les éléments de base.table.
    Tu dis que tu veux tous les éléments de base.table, linq te les ramène...
    Ce que je veux dire, c'est que si je veux par exemple exécuter ce genre de truc :

    Ramener le premier élément de la table qui corresponde à ma requête "EstDansLeTableau", et que ma table contienne des dizaines de milliers d'enregistrement, je risque de tomber à court de RAm alors que je n'ai besoin que d'un seul élément.

    Ca revient à dire que linq ne fait rien d'autre d'utile que de ramener toute la table de la base en Ram, et je connais peu de programmeurs qui vont ramener une Bdd en Ram pour y faire des recherches.
    A la limite, c'était plus efficace ce que je faisais avant de connaître linq et C# : je créais un fichier à accès direct et je me balladais dedans en y faisant tous les tests possibles et imaginables sans limite aucune et sans rien importer d'autre que l'objet en cours de test.

    Le truc c'est que dans ton cas tu n'as pas besoin de te poser la question.
    Ben si, parce que je n'ai pas évidemment donné le programme entier, je n'ai mis qu'un exemple. J'ai d'autres tests à faire du même genre.

    Tu veux récupérer tous les clients donc les NumClient ne sont pas contenus dans tabls1 et tabl2. C'est bien ça ?
    Cette requêtes est tout à fait traductible en SQL. Ta méthode EstDansLeTableau ne l'ait pas (comme on te l'a expliqué au début) mais la requête en elle même si. C'est un simple SELECT * FROM CLIENT WHERE NUMCLIENT NOT IN ('146', '1258', '8756').

    En Linq ça donne ça:
    ..
    PS: je n'ai pas testé le code
    C'est une réponse constructive, malheureusement ça ne fonctionne pas d'avantage. Voici le message d'erreur retourné à l'exécution :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    LINQ to Entities ne reconnaît pas la méthode « Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String) », et cette dernière ne peut pas être traduite en expression de magasin.
    PS2: je ne trouve pas très top de stocker des long dans un tableau de string
    Je comprends parfaitement, mais de nouveau ce n'est qu'un bout de code. Ce tableau provient du split d'une chaîne de caractères qui peut être une combinaison d'introduction manuelle de l'utilisateur et d'introduction automatique via des choix de listes etc. Au final, l'utilsateur obtient une chaîne du type : "120,125,201,305,466"
    qui, une fois splittée donne le tableau dont on parle.

    Si tu fais un foreach sur une requête qui renvoie 1 million de lignes, il ne va pas créer 1 million d'objets avant de commencer à les renvoyer à l'énumérateur, sinon ce serait impossible de faire des traitements de masse à moins d'avoir une RAM énorme
    C'est bien ça qui m'inquiète, d'où ma question si la requête est unique : je procède comment pour obliger la base à rapatrier un élément à la fois? J'opère "manuellement" dans une boucle de requête avec recherches sur la clé? Ca, par contre, ça oblige le moteur à rechercher dans la base pour chaque itération.
    Parce que si ça fonctionne comme ça, je n'ai en fait qu'une façon de procéder :

    1) Je fais tous mes tests transformables en sql, mais je ne rappatrie qu'un tableau de clé d'index.
    2) Je fais un foreach dans le tableau de clé, en commençant par une recherche sur le numéro de clé puis mes tests sur l'élément rapatrié.

    Sinon, j'ai jeté un oeil sur la vidéo dlinq venant de chez Microsoft, avec un exemple de requête suivie de "foreach".

    Pierre Lagarde dit déjà, ce qui est amusant : "Quand est-ce que la requete est exécutée? en fait c'est très clair..."
    puis il dit :

    "Quand le foreach commence à sa faire sa boucle, dans un ienumerator avant de parcourir le premier élément, il génère sa requête."

    Jusque là, c'est clair et conforterait la vision "retour en ram".

    Mais il précise :

    "Ensuite, il fait une boucle while qui contient un yield return" qui renvoie un reader"

    "Il exécute la requête dans un ienumarator, il récupère le curseur sur la requête, il fait son while jusqu'à ce qu'il arrive au bout de son reader, et pour chacun des éléments il fait un yield return, et il construit l'objet qu'il renvoie dans le select."

    Il ajoute en montrant la requête précédent le foreach : "Ici on est en train de construire le reader, et on en ressort avec le yield".

    Ce qui conforterait l'option "un par un", non?

    C'est sensé être très clair, mais on aurait pu exprimer ça pour que ça ne puisse pas être compris de façon ambigüe.

    Où est la réalité?

    Claude

  15. #15
    Membre chevronné
    Inscrit en
    Octobre 2005
    Messages
    400
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 400
    Par défaut
    Pour réaliser des requêtes complexe de sélection, linq to Entity n'est pas très efficace. Outre les fonctions non supportés, les requêtes générées peuvent être très coûteuse. Il est certain que par moment, tu soit contrains d'utiliser ADO.Net comme à l'ancienne.
    Une autre technique est de passer par une vue. Cela va te créer une nouvelle entité entité, et va te permettre de requêter la vue grâce à des requêtes Linq.

  16. #16
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    LINQ to Entities ne reconnaît pas la méthode « Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String) », et cette dernière ne peut pas être traduite en expression de magasin.
    Le provider EF pour SQLite ne doit pas gérer les requêtes "IN" je suppose... mais vu que ce provider évolue assez rapidement, c'est possible que ça ait été ajouté depuis. Essaie de récupérer la dernière version pour voir

    Citation Envoyé par ClaudeBg Voir le message
    "Quand le foreach comment à sa faire sa boucle, dans un ienumerator avant de parcourir le premier élément, il génère sa requête."

    Jusque là, c'est clair et conforterait la vision "retour en ram".

    Mais il précise :

    "Ensuite, il fait une boucle while qui contient un yield return" qui renvoie un reader"

    "Il exécute la requête dans un ienumarator, il récupère le curseur sur la requête, il fait son while jusqu'à ce qu'il arrive au bout de son reader, et pour chacun des éléments il fait un yield return, et il construit l'objet qu'il renvoie dans le select."

    Il ajoute en montrant la requête précédent le foreach : "Ici on est en train de construire le reader, et on en ressort avec le yield".

    Ce qui conforterait l'option "un par un", non?
    Effectivement, sauf que "DLinq" (qui ne s'appelle plus comme ça d'ailleurs), c'est Linq to SQL pas Linq to Entities... mais je pense que le principe est le même

  17. #17
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    Citation Envoyé par oyigit Voir le message
    Une autre technique est de passer par une vue. Cela va te créer une nouvelle entité entité, et va te permettre de requêter la vue grâce à des requêtes Linq.
    Si j'ai bien tout compris, cette solution ne fonctionne pas avec SQLite

    Essaie de récupérer la dernière version pour voir
    Ce n'est pas facile de s'y retrouver, on trouve Sqlite dans plusieurs sortes de package. J'ai téléchargé ici : http://sqlite.phxsoftware.com/ et j'ai la dernière version présente.

    sauf que "DLinq" (qui ne s'appelle plus comme ça d'ailleurs), c'est Linq to SQL pas Linq to Entities... mais je pense que le principe est le même
    C'est ce que je me suis dit : que lorsqu'on utilise linq to entities sur une base, on se retrouve quelque part devant les mêmes problèmes que lorsqu'on utilise linq to sql. La vidéo que je possède date de l'avant-projet de C#3 et de linq, c'est pourquoi c'est toujours l'ancien nom.

    Mais bon, j'en suis toujours au stade des suppositions, je continue de mon côté de chercher une réponse, tout en vérifiant si quelqu'un ici a une information complémentaire.

    Claude

  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par ClaudeBg Voir le message
    Ce n'est pas facile de s'y retrouver, on trouve Sqlite dans plusieurs sortes de package. J'ai téléchargé ici : http://sqlite.phxsoftware.com/ et j'ai la dernière version présente.
    C'est bien de celui là que je parle...

    Eventuellement tu peux poser la question sur le forum. Robert Simpson (l'auteur du provider SQLite) est assez réactif en général... cela dit ça fait 6 mois qu'il a pas sorti une nouvelle version, alors qu'avant y en avait au moins une par mois

  19. #19
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Par défaut
    Merci pour les renseignements

    De mon côté, j'essaye d'obtenir confirmation du fonctionnent du foreach, si j'ai des informations, je les communique.

    De votre côté, si quelqu'un a des informations complémentaires, ça m'intéresse vivement.

    Merci

    Claude

Discussions similaires

  1. linq to sql, procédure stockée
    Par solo190 dans le forum C#
    Réponses: 0
    Dernier message: 01/09/2011, 12h57
  2. Différence de procédures FASM/TASM
    Par CP / M dans le forum Assembleur
    Réponses: 3
    Dernier message: 30/07/2008, 16h17
  3. Différence entre macros et procédures ?
    Par noubigh dans le forum Assembleur
    Réponses: 7
    Dernier message: 11/02/2007, 01h12
  4. Mysql5: différences entre procédures et fonctions
    Par El Riiico dans le forum SQL Procédural
    Réponses: 1
    Dernier message: 25/11/2005, 06h43
  5. Différence, exemple procédural, événementiel, objet ?
    Par ludophil dans le forum Débuter
    Réponses: 3
    Dernier message: 26/10/2005, 09h35

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