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

Accès aux données Discussion :

[Linq to Sql] [C#] Propriété de navigation


Sujet :

Accès aux données

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

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut [Linq to Sql] [C#] Propriété de navigation
    Bonjour,

    Voici mon problème :

    J'ai une table "Client" et une table "Adresse"

    Il y a une relation entre les deux tables (Client.Adresse, Client.AdresseReference) bien visible graphiquement.

    Je recherche un Client dans la base -> monClient
    Je recherche une Adresse dans la base -> monAdresse

    Je n'arrive pas en fait à créer la relation entre le client et son adresse.

    Si j'écris :

    monClient.Adresse = monAdresse

    Ca fonctionne tant que je ne ferme pas mon programme (en fait, apparemment la liaison se fait avec l'adresse en mémoire, pas avec l'adresse dans la table).

    Si je réouvre mon programme, je ne sais plus joindre les propriétés de l'adresse du client (monClient.Adresse.rue par exemple) parce que monClient.Adresse vaut "null".

    Je n'arrive pas à trouver la bonne syntaxe, j'ai essayé en utilisant monClient.AdresseReference également, ou la méthode attach, mais pas moyen de trouver.

    La réponse est sûrement toute bête, mais j'ai du louper quelque chose

    Merci d'avance

    Claude

  2. #2
    Membre émérite
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Avril 2006
    Messages
    1 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 1 627
    Points : 2 331
    Points
    2 331
    Par défaut
    Appels à SaveChanges() correctement faits ?

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

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

    Oui, voici une partie du code concerné (à cet endroit on a un Client "newClient" correctement initialisé excepté qu'on ne lui a pas affecté d'adresse, et une adresse "newAdresse" initialisée excepté sa clé primaire "Id" ):
    Trouver est une méthode d'extension renvoyant l'adresse éventuellement trouvée dans la table.
    Ce code s'exécute sans recevoir les messages d'erreur prévus, l'adresse se retrouve bien dans la table adresse en évitant les doublons, et le client dans la table client.

    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
     
                                                // GERER L'ADRESSE
                                                // ---------------
                Adresse ad = newAdresse.Trouver();                  // chercher si l'adresse existe déjà
                if (ad != null)                                     // si oui
                {
                    newClient.Adresse = ad; 
                }
                else                                                // s'il s'agit d'une nouvelle adresse
                {
                    try
                    {
                        newAdresse.SetNewId();                      // affecter une clé primaire libre
                        Bdd.AddToAdresse(newAdresse);               // ajouter l'adresse
                        Bdd.SaveChanges();                          // sauver modifications
                        newClient.Adresse =                         // affecter l'adresse enregistrée au client
                             (from a in Bdd.Adresse
                              where a.Id == newAdresse.Id
                              select a).FirstOrDefault();
                    }
                    catch (Exception ex1)                           // erreur 
                    {
                        MessageBox.Show("Une erreur est survenue lors de l'écriture de l'adresse du client:\n\n"
                            + ex1.Message, "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return false;
                    }
     
                }
                                                // GERER LE CLIENT
                                                // ---------------
                try
                {
                    Bdd.AddToClient(newClient);                 // ajouter le client
                    Bdd.SaveChanges();                          // sauver modifications
                }
                catch (Exception ex2) 
                {
                    MessageBox.Show("Une erreur est survenue durant l'écriture du client:\n\n"
                        + ex2.Message, "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
    Lors de l'affichage, si je tente d'afficher le client et que je mets un point d'arrêt dans ma méthode d'affichage (figure "Avant"), on voit bien que tout est correctement initialisé.

    Ensuite, je quitte le programme, je demande le réaffichage du même client en passant pas les mêmes méthodes, et le même point d'arrêt (figure "Apres") montre que le champs "adresse" du client est maintenant null. Je comprends mal pourquoi ces différences de comportement.

    merci d'avance

    Claude
    Images attachées Images attachées   

  4. #4
    Membre émérite
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Avril 2006
    Messages
    1 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 1 627
    Points : 2 331
    Points
    2 331
    Par défaut
    Un problème de contexte vraisemblablement.
    Peut-on voir le code de la fonction Trouver ? Le reste me semble juste

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

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

    Aucun problème, voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
            public static Adresse Trouver(this Adresse adresse)
            {
                return 
                    (from a in Bdd.Adresse
                     where (a.Boite.ToLower() == adresse.Boite.ToLower()
                     && a.CodeP.ToLower() == adresse.CodeP.ToLower()
                     && a.Localite.ToLower() == adresse.Localite.ToLower()
                     && a.Numero.ToLower() == adresse.Numero.ToLower()
                     && a.Pays.ToLower() == adresse.Pays.ToLower()
                     && a.Rue.ToLower() == a.Rue.ToLower())
                     select a).FirstOrDefault();
            }
    C'est tout bête (tous les champs sont non nullables)
    Note que si j'ajoute un client dont l'adresse n'existe pas, cette méthode renvoie null et donc le résultat n'est pas utilisé.

    J'ai vérifié, que la méthode retourne null ou non, dans les deux cas j'ai strictement le même phénomène.

    Bref, que j'affecte à mon client l'adresse retournée par cette méthode ou la nouvelle adresse créée puis relue de la base, c'est pareil.

    Vu que je commence à me demander si le problème vient de l'enregistrement ou de la relecture, voici la méthode qui me renvoie le client qui a le numéro le plus élevé. La méthode d'affichage dont j'ai donné une partie du code teste évidemment si le client est null :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            private void AfficheClientDern()
            {
                AfficheClient(
                    (from c in Bdd.Client
                    orderby c.NumClient descending
                    select c).FirstOrDefault());
            }
    Je peux donner le code de toute la form s'il faut, il n'y a pas de secret défense, c'est destiné de toutes façons ensuite à être "open-source".
    C'est juste que j'essaye de cerner au max le problème pour éviter de faire perdre du temps inutile à ceux qui veulent bien m'aider.


    Bon, je viens en faite juste de m'apercevoir que le problème venait du select et non de l'enregistrement.

    Si je remplace le bout de code ci-dessus par ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
            private void AfficheClientDern()
            {
                var test =
                    (from c in Bdd.Client
                     orderby c.NumClient
                     select new {c.Nom,c.Adresse.Localite }).FirstOrDefault();
     
                MessageBox.Show(test.Localite);
            }
    C'est à dire en demandant le renvoi explicite d'un champs de l'adresse dans une nouvelle structure, ça fonctionne et le messagebox me donne bien le nom de la localité du client.

    Bref, le client a correctement été enregistré avec son adresse, mais je ne récupère pas l'adresse lors du select fait simplement sur le client.

    Pourtant, logiquement, la propriété de navigation "Adresse" devrait être un simple pointeur, je ne comprends pas pourquoi la valeur de ce pointeur n'est pas rapatriée.

    Et si je suis contraint d'inventer un nouveau type de client pour renvoyer un client avec une adresse, ça va devenir très lourd à gérer, surtout si on a une table qui pointe sur une autre table elle-même pointant sur une troisième table.

    Y a-t-il une autre méthode?
    Et intuitivement j'ai le sentiment qu'il faudrait utiliser le champs "AdresseReference" qui a été ajouté automatiquement par linq to entities, et qui ressemble furieusement dans sa dénomination à un pointeur. Mais j'ai essayé plusieurs manipulations avec ce champs, je n'arrive à rien de concluant.

    Merci

    Claude

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

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

    J'ai fini par trouver, après m'être arraché les cheveux.
    Ca venait bien du select, il fallait inclure la table pointée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            private void AfficheClientDern()
            {
                AfficheClient(
                    (from c in Bdd.Client.Include("Adresse")
                     orderby c.NumClient descending
                     select c).FirstOrDefault());
            }
    Je trouve ça curieux de devoir préciser la table pointée lorsqu'on rapatrie un simple pointeur, mais puisque ça fonctionne...

    Seul problème, le nom de la table passé en "bête" string reporte la vérification à l'exécution et plus à la compilation, ce qui est dommage. Mais bon, peut-être que ce sera résolu à la nouvelle version?

    Merci de t'être intéressé à mon problème, et vraiment désolé pour le dérangement

    Merci
    Claude

  7. #7
    Membre émérite
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Avril 2006
    Messages
    1 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 1 627
    Points : 2 331
    Points
    2 331
    Par défaut
    Ok je vois.

    Un alternative aurait été : monClient.Adresse.Load().
    Par défaut EF ne charge que l'objet cible, et non pas les objets liés. il faut donc les charger explicitement de cette manière, soit implicitement Bdd.Addresse.ToList(), EF faisant alors l'association entre les différents objets.

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

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    Oui, merci.

    A l'époque, j'ai créé des petites BDD en C, et j'y manipulais des pointeurs. J'ai pensé que "Adresse" était un simple pointeur vers l'adresse de la table "Adresse" correspondante, et donc rapatrié d'office, et manifestement ce n'est pas le cas, c'est plus complexe que ça.

    Une fois mis sur la bonne piste, j'ai finalement implémenté "Load" dans ma méthode d'affichage.

    Si j'ai bien tout assimilé :

    "Include" fait une jointure des tables "Client" Et "Adresse":

    Avantage : si mon sélect me renvoie une liste de "Client" conséquente sur laquelle j'ai besoin d'avoir accès à l'adresse, j'ai tout en une seule requête.

    Inconvénient : j'obtiens une table jointe où il pourrait y avoir des redondances au niveau des noms et prénoms du client (ce n'est pas mon cas, puisque une seule adresse par client, mais ça pourrait l'être dans un autre cas). De plus, ça crée des tables assez conséquentes et longues à construire.

    "Load" ajoute la référence uniquement pour l'objet obtenu précisé en paramètre.

    Avantage : Pas de création d'une jointure, et donc select plus rapide

    Inconvénient : une requête supplémentaire pour chaque objet retourné par le select dont on veut manipuler l'adresse.

    Toujours si j'ai bien compris :

    J'utilise "include" pour les requêtes me renvoyant un grand nombre de clients dont je dois utiliser l'adresse (listage dans un binding par exemple)

    J'utilise "Load" pour les requêtes me renvoyant peu ou un seul client dont je dois utiliser l'adresse (le cas de mon affichage de client).

    Si j'ai mal compris, ne pas hésiter à me le dire, ça me rend service

    Je profite de la présente pour poser une question subsidiaire : Avant, j'avais mes adresses intégrées aux client, aucun problème pour l'affichage dans un bindingView.

    Depuis que j'ai scindé, si je déclare mes colonnes comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
                dataGridView.Columns["Nom"].DataPropertyName = "Nom";   // affecter les colonnes aux proprités de Client
                dataGridView.Columns["Prenom"].DataPropertyName = "Prenom";
                dataGridView.Columns["NumClient"].DataPropertyName = "NumClient";
                dataGridView.Columns["CodePostal"].DataPropertyName = "Adresse.CodeP";
                dataGridView.Columns["Ville"].DataPropertyName = "Adresse.Localite";
                dataGridView.Columns["Rue"].DataPropertyName = "Adresse.Rue";
                dataGridView.Columns["Numero"].DataPropertyName = "Adresse.Numero";
                dataGridView.Columns["Informations"].DataPropertyName = "Info";
     
                // doit se trouver après création des colonnes, sinon les colonnes ne se redimensionnent pas
                bindingSource.DataSource = clients;                     // affecter la liste clients comme source du binding
                dataGridView.DataSource = bindingSource;                // affecter ce binding au datagrid
                bindingNavigator.BindingSource = bindingSource;         // ainsi qu'au bindingnavigator
    Aucun des champs relatif à "Adresse" ne s'affiche (Adresse.Rue par exemple).

    Si je mets "Rue" tout court, ça ne fonctionne pas non plus.

    Si je mets un point d'arrêt, mes clients contiennent pourtant bien leur adresse (méthode include).

    Il y a une astuce?

    Merci

    Claude

  9. #9
    Membre émérite
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Avril 2006
    Messages
    1 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 1 627
    Points : 2 331
    Points
    2 331
    Par défaut
    Citation Envoyé par ClaudeBg Voir le message
    Inconvénient : j'obtiens une table jointe où il pourrait y avoir des redondances au niveau des noms et prénoms du client (ce n'est pas mon cas, puisque une seule adresse par client, mais ça pourrait l'être dans un autre cas).
    Tant que tu es dans le même datacontext, tu manipuleras toujours la même référence pour la même adresse. Si tu charges 10 fois l'adresse, voir 10 clients avec la même adresse, la référence sera toujours la même, magie du context. Dès que tu es sorti de ton context, que tu en recrée un autre, et que tu refais une demande, la référence sera nouvelle.

    Cela pose des soucis avec WCF, où immanquablement les références sont différentes et donc oblige à un certain suivi.

    Pour ton souci de bindingview je ne sais pas.

    Remarque : on ne peut chainer les includes.
    Client => Adresse => Ville => Pays, dans ce cas là, s'il y a besoin de tout,
    soit tu fais un parcours de chaque élezment et un appel à Load, soit un appel à ToList sur chaque objet, le context s'occupant des références.

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

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    Merci pour le complément d'information

    Ne me reste plus pour l'instant que mon problème de bindingview, je n'ai pas encrore trouvé

    Claude

    Edité :

    Bon, pour ceux qui rencontreraient le même problème, j'ai réussi à contourner ce problème en créant dans la form un dataset que j'initialise avec les champs des deux tables. Ensuite, j'affecte ce dataset au bindingsource. C'est un peu plus lourd, mais ça fonctionne.

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

Discussions similaires

  1. Linq, Propriété de navigation et temps de réponse
    Par sephirostoy dans le forum Linq
    Réponses: 8
    Dernier message: 15/02/2011, 14h35
  2. Probleme transfert object sql 2005 - propriété login
    Par sizzla68 dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 17/11/2008, 10h32
  3. Réponses: 2
    Dernier message: 25/08/2008, 10h54
  4. [SQL] PB bouton de navigation de pages
    Par megapacman dans le forum PHP & Base de données
    Réponses: 3
    Dernier message: 18/05/2006, 13h11
  5. Réponses: 6
    Dernier message: 11/04/2006, 10h56

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