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

NHibernate Discussion :

Problème de comportement DataGridView.DataSource + Objets (et NHibernate)


Sujet :

NHibernate

  1. #1
    Candidat au Club
    Inscrit en
    Juin 2002
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 10
    Points : 3
    Points
    3
    Par défaut Problème de comportement DataGridView.DataSource + Objets (et NHibernate)
    Bonjour,

    Je suis en train de développer une petite application C# avec des DataGridView et j'ai un problème assez embêtant.

    Dans un Form, j'ai un DataGridView que je rempli avec des objets de type Person.

    Une Person ressemble à ceci (je mets les propriétés en public pour réduire le code):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Person {
      public int id;
      public string name;
      public ISet<Address> addresses = new HashedSet<Address>();
    }
     
    public class Address {
      public int id;
      public Person person;
      public string address;
    }
    En gros, une Person a une liste d'Address.

    Imaginons que j'ai les donées suivantes (toutes les données viennent d'une base de donnée avec NHibernate):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Person p1:
      - id: 1
      - name: TOTO
      - address:
          * id: 1
          * address: my house
          * id: 2
          * address: my office
    Mon application a 2 Form. Un sur lequel se trouve un DataGridView avec mes Person, l'autre sur lequel se trouve un DataGridView avec les Address.

    Pour remplir la 1e grille, j'utilise le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    IList<Person> listPersons = new List<Person>(...);
    myPersonGrid.DataSource = listPersons;
    Lorsque je click sur un élément de ma grille, j'exécute le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Person p = myPersonGrid.CurrentRow.DataBoundItem as Person;
    AddressForm myAddressForm = new AddressForm(); //Form qui contient le 2e DataGridView
    myAddressForm.person = p; //Je passe l'objet Person au Form
    myAddressForm.ShowDialog();
    Dans le 2e Form, je remplis la grille de la manière suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    IList<Address> listAddresses = new List<Address>(person.addresses);
    myAddressGrid.DataSource = listAddresses;
    Dans ce Form, j'ai 2 boutons (SAVE et CANCEL).
    Le SAVE sauve les modifications dans la BD.
    Le CANCEL ferme le Form.

    Lorsque je veux éditer une addresse, j'appelle le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Address a = myAddressGrid.CurrentRow.DataBoundItem as Address;
    a.address = addressTextBox.Text; // contrôle sur le Form
    Si je click sur SAVE, je vois dans le log de NHibernate que l'objet Address a bien été modifié. Donc si je ferme le 2e Form et que je click à nouveau sur ma 1e grille, je verrai bien l'addresse modifiée dans le 2e Form.

    Maintenant, voici mon problème...
    Si j'édite une addresse de mon patient, que je click sur CANCEL (donc les modifications ne sont pas sauvées) et que j'ouvre à nouveau mon 2e Form, mes modifications seront toujours là!
    J'ai l'impression que c'est lié au comportement du DataSource mais je ne comprends pas car je crée à chaque fois une nouvelle List<Address> à chaque fois que j'ouvre le 2e Form.

    Serait-ce aussi un problème lié à NHibernate?

    Merci d'avance,

    Nasedo47

  2. #2
    Rédacteur
    Avatar de Paul Musso
    Profil pro
    Inscrit en
    Août 2008
    Messages
    368
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Août 2008
    Messages : 368
    Points : 443
    Points
    443
    Par défaut
    Bonjour,

    Essaie de rajouter ça quand tu cancel ta 2ème form :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    myDataGrid.EditItemIndex = -1;     
    BindData();

  3. #3
    Candidat au Club
    Inscrit en
    Juin 2002
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 10
    Points : 3
    Points
    3
    Par défaut
    Bonjour,

    Je ne trouve pas la propriété EditItemIndex pour mon DataGridView.

    Pour info, je travaille avec SharpDevelop et .NET 2.0.

  4. #4
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    @Paul : ce que tu suggères n'est pas valable pour la DataGridView des winforms

    Ton problème vient de là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    IList<Address> listAddresses = new List<Address>(person.addresses);
    ...
    Address a = myAddressGrid.CurrentRow.DataBoundItem as Address;
    a.address = addressTextBox.Text; // contrôle sur le Form
    En effet, tu ne copies la liste d'adresse. Ta "listAdresses" contient les mêmes éléments que person.addresses, parce que Addresse est une classe. Tu as fait une "shallow copy" là où il faudrait une "deep copy".

    Donc là, dans ton code, c'est directement un adresse que tu modifies, pas une copie.

    Il doit y avoir des patterns bien connus pour résoudre ce souci ; perso, ce que je fais, c'est que toute fenêtre permettant d'éditer un objet commence par cloner complètement l'objet source, de façon à binder ce clone à l'interface, et à n'assigner cet objet modifié à l'objet d'origine que si l'user valide la form.

    Tu as aussi l'interface IEditableObject ( http://msdn.microsoft.com/fr-fr/libr...ct(VS.80).aspx ), qui permet d'annuler des modifs que tu as pu faire sur ton objet.
    ಠ_ಠ

  5. #5
    Candidat au Club
    Inscrit en
    Juin 2002
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 10
    Points : 3
    Points
    3
    Par défaut
    Je ne comprends pas trop comment faire pour utiliser l'interface IEditableObject.

    J'ai fait les modifications suivantes dans mon code:

    Lorsque je click sur une Person dans mon 1e DataGridView:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    myAddressForm.person = Person.Clone(p);
    Contenu de la méthode Clone() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public static Person Clone(Person p)
    {
      Person cloneP = (Person)p.MemberwiseClone();
      return cloneP;
    }
    Ca me fait le même résultat.

    Est-ce que je dois faire une méthode Clone() beaucoup plus poussée?
    En fait, ma classe Person contient en réalité plus de 20 propriétés (types standards ou autres objets que j'ai créés) et 10 ISet<T> (dont Addresses).

  6. #6
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    C'est parce que ta copie est pas assez profonde.

    le MemberCloCkWise fait en gros :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Personne p = new Personne();
    p.Id = this.Id;
    p.Truc = this.Truc;
    ...
    p.Adresses = a.Adresses;
    Ce qui fait que ton ancien et ton nouvel objets contiennent tous les deux les mêmes adresses.

    Vois-tu la différence entre une struct et une class ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    A a1 = new A();
    a1.Nom = "x";
    A a2 = a1;
    a2.Nom = "y";
    Si A est une classe, alors manipuler a1 et a2 est exactement la même chose, parce que a1 et a2 ne sont que des références vers l'objet. Tu as alors a1.Nom = a2.Nom = "y".

    Si A est une struct, alors la ligne A a2 = a1; n'est pas une copie de référence, mais une copie complète de la struct. a1 et a2 sont alors deux objets distincts.

    Donc même si tu clones ta classe personne, tes deux objets ont des listes qui réfrence les mêmes objets ; p1.Adresses[0].Nom = "x" est strictement équivalent de p2.Adresses[0].Nom = "x".
    ಠ_ಠ

  7. #7
    Candidat au Club
    Inscrit en
    Juin 2002
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 10
    Points : 3
    Points
    3
    Par défaut
    En fait, je ne savais pas qu'il existait un sytème de shallow copy et deep copy.

    Après quelques recherches, j'ai créé la méthode suivante dans ma classe Person:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    		public virtual object Clone()
    		{
    			MemoryStream ms = new MemoryStream();
    			BinaryFormatter bf = new BinaryFormatter();
    			bf.Serialize(ms, this);
    			ms.Position = 0;
    			object o = bf.Deserialize(ms);
    			ms.Close();
    			return o;
    		}
    J'ai du mettre [Serializable] en haut de chacune de mes classes.

    Ca a l'air de fonctionner, mais maintenant, le problème est du côté de NHibernate car j'utilise l'option lazy=true pour mes collections, donc, elles ne sont pas clonées en même temps que l'objet Person étant donné qu'elles n'existent pas encore (car pas "fetchées"). En mettant lazy=true, cela fonctionne, mais j'aimerais trouver une autre solution...

    Déjà merci pour l'aide fournie jusqu'à présent

  8. #8
    Candidat au Club
    Inscrit en
    Juin 2002
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 10
    Points : 3
    Points
    3
    Par défaut
    Avec ma méthode Clone(), je n'ai plus mon problème lié au DataGridView. Par contre, j'en ai un nouveau qui doit être lié à cela...

    Dans mon 2e Form, j'ai une comboBox qui représente une liste de Gender.
    Je l'initialise comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    myGenderCB.DataSource = session.List<Gender>(); //pour faire simple car je fait appelle à divers fonctions qui se connectent à la base et me retourne une List<Gender>
    Mon objet Perso à une propriété Gender;

    Dans mon 2e Form, je fais:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    myGenderCB.SelectedItem = myPerson.Gender;
    Il ne positionne pas le CB sur la bon élément.
    Par contre, si je fais une shallow copy de mon objet Person, cela fonctionne...

Discussions similaires

  1. Problème de conversion Datagridview.datasource
    Par padrinho dans le forum VB.NET
    Réponses: 4
    Dernier message: 06/08/2009, 19h22
  2. [Coldfusion] Problème de comportement des *.cfm
    Par DarkOcean dans le forum Coldfusion
    Réponses: 7
    Dernier message: 21/06/2006, 18h52
  3. [SAX] Problème SAX (comportement de characters)
    Par philmo dans le forum Format d'échange (XML, JSON...)
    Réponses: 4
    Dernier message: 29/09/2005, 11h46
  4. [TTreeView] Problème avec les pointeurs d'objet
    Par BlackWood dans le forum Composants VCL
    Réponses: 2
    Dernier message: 02/07/2004, 14h31
  5. [C#] Problème pour l'appel d'objet...
    Par AntiSAL dans le forum Windows Forms
    Réponses: 2
    Dernier message: 14/06/2004, 09h59

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