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

Entity Framework Discussion :

CodeFirst EF4.3.1 - Update non réalisé en base


Sujet :

Entity Framework

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 7
    Points : 5
    Points
    5
    Par défaut CodeFirst EF4.3.1 - Update non réalisé en base
    Bonsoir,

    Je souhaite ajouter, supprimer et modifier un livre par le biais de 2 classes en utilisant CodeFirst (utilisation d'une base existante) :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public class Livre
        {
              public int LivreID { get; set;}
               public string Titre {get; set;}
               public int  AnneeEdition { get; set;}
     
               public virtual Auteur AutLivre { get; set;}
    }
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public class Auteur
        {
               public int AuteurID {get; set;}
               public string Nom { get; set; }
    }

    L'ajout et la suppression d'un livre fonctionnent : la modification de l'auteur d'un livre n'est pas prise en compte dans la base. L'update se fait bien car le titre est modifié en base.
    Je ne souhaite pas ajouter la FK dans la classe Livre (j'ai testé , l'Update fonctionne)

    Dans la méthode OnModelCreating, j'ai configuré :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    modelBuilder.Entity<Livre>()
                   .HasRequired(p => p.AutLivre)
                   .WithMany()
                   .Map(x => x.MapKey("AuteurId"));

    AuteurId est le nom de la colonne FK de la table Livre.

    Voici la dernière version de ma méthode Update: la propriété AutLivre a été chargée par la sélection d'un objet Auteur dans une combo.

    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
    public void Update(Livre item)
    {     using (BiblioContext db = new BiblioContext())
             {  try
                      { int idA = item.AutLivre.AuteurID;
                           var aut = db.Auteurs.Single(c => c.AuteurID == idA); 
                           item.AutLivre = aut;
                           db.Entry(item).State = EntityState.Modified;
                           int n = db.SaveChanges();[/INDENT]
             }
             catch (Exception ex)
             {
                    throw new Exception(ex.Message);                  
    }      
    }
    }

    Sur certains posts, on orientait vers cette configuration

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    HasRequired(p => p.AutLivre)
                   .WithMany()
                   .IsIndependent()
                   .Map(x => x.MapKey(a => a.AuteurId, "AuteurId"));

    que je n'arrive pas à coder : je ne trouve pas cette méthode IsIndependant()!

    Je commence à vraiment tourner en rond !
    Auriez-vous des idées ?
    Merci par avance

  2. #2
    Membre confirmé

    Profil pro
    Développeur .NET
    Inscrit en
    Août 2004
    Messages
    178
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2004
    Messages : 178
    Points : 645
    Points
    645
    Par défaut
    Bonjour,

    Ta méthode Update fonctionne lorsque tu ajoute la FK dans la classe Livre ?

  3. #3
    Membre confirmé

    Profil pro
    Développeur .NET
    Inscrit en
    Août 2004
    Messages
    178
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2004
    Messages : 178
    Points : 645
    Points
    645
    Par défaut
    J'ai fais quelques tests.

    A noter : Je n'ai pas utilisé comme toi l'API Fluent, je n'ai donc pas de méthode OnModelCreating.

    Ceci dit, même sans API Fluent, j'ai eu le même soucis avec cette fonction:

    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
    public int Update(Livre item)
            {
                int affectedRow = 0;
     
                using (BiblioContext db = new BiblioContext())
                {
                    try
                    {
                        Auteur aut1 = db.Auteurs.Find(2);
                        item.AutLivre = aut1;
                        db.Entry(item).State = EntityState.Modified;
                        affectedRow = db.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message);
                    }
                }
     
                return affectedRow;
            }
    Le debug m'a fait tilté sur un message d'erreur, j'ai donc modifié ma fonction:

    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
     public int Update(int idLivre)
            {
                int affectedRow = 0;
     
                using (BiblioContext db = new BiblioContext())
                {
                    try
                    {
                        Livre item = db.Livres.Find(idLivre);
     
                        Auteur aut1 = db.Auteurs.Find(2);
                        item.AutLivre = aut1;                    
                        db.Entry(item).State = EntityState.Modified;
                        affectedRow = db.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message);
                    }
                }
     
                return affectedRow;
            }
    Et là ça marche.

    L'explication ? Nous déclarons un context local à la fonction, la portée des variables étant ce qu'elle est, à l'issue de la fonction qui t'a retourné le livre que tu as en paramètre, le context utilisé dans cette fonction n'est plus.

    Quel est l'impact ? Le context ne fait pas que te renvoyer un objet, ça, c'est l'impression que l'on a.

    Mais en creusant, on peut constater ceci:


    Comme tu peux le voir, c'est un proxy qui est créé.

    Je ne maitrise pas entièrement la mécanique derrière ce processus, mais tu peux deviner que le 1er contexte étant en fin de vie à la fin de la fonction, cela altère le proxy créé, d'où ton erreur.

    Une solution simple consiste à travailler avec l'id et à récupérer le livre à partir du contexte qui fait la mise à jour, comme dans la fonction que j'ai modifiée.

    Bon courage
    Images attachées Images attachées  

  4. #4
    Futur Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Bonsoir et merci pour tes réponses ,

    Oui, la méthode Update fonctionne lorsque la FK est ajoutée dans la classe .
    Mais j'ai l'impression de complètement dénaturer mes Poco, d'autant plus qu'avec Nhibernate, ça marche !

    Je ne vois pas comment mettre en oeuvre la solution que tu me proposes, car entre la recherche du livre (Find) et sa mise à jour, les attributs du livre sont modifiés par l'utilisateur.
    De plus, je travaille en n-tiers (Bll + Dal) ...

    J'ai du mal à croire que ce n'est pas possible, mais j'avoue que je me casse les dents !

    Merci encore

  5. #5
    Membre confirmé

    Profil pro
    Développeur .NET
    Inscrit en
    Août 2004
    Messages
    178
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2004
    Messages : 178
    Points : 645
    Points
    645
    Par défaut
    Peut-être que je n'ai pas tout compris, mais lorsque tu souhaites faire ta mise à jour, tu appelles bien ta méthode Update(Livre item).

    Qu'est-ce qui t'empêche de passer Livre.LivreId au lieu de Livre en paramètre de ta méthode Update ?

    car entre la recherche du livre (Find) et sa mise à jour, les attributs du livre sont modifiés par l'utilisateur.
    C'est instantané... Je vois mal l'utilisateur modifier l'objet entre le find et le SaveChange, quelque chose doit m'échapper...

    Sinon c'est compatible avec ton architecture sans soucis, j'ai déjà fais du code first en dal + bll + ihm + dto.

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    J'ai recodé ma méthode avec ta solution :

    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
      public void Update(Livre oModifie)
            {
                using (BiblioContext db = new BiblioContext())
                {
                    try
                    {
                         // recherche original 
                         Livre oOriginal = db.Livres.Find(oModifie.LivreID);
     
                         // Recherche auteur modifié et affectation à celui qui sera sauvé 
                         int idA = oModifie.AutLivre.AuteurID;
                         var aut = db.Auteurs.Find(idA);
                         oOriginal.AutLivre = aut;
                         // si proprietes livre modifiees ==> 
                         oOriginal.Titre = oModifie.Titre;
                         oOriginal.AnneeEdition = oModifie.AnneeEdition;
                        // maj du livre obtenu par Find
                         db.Entry(oOriginal).State = EntityState.Modified;
                         int n = db.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message);
                    }
                }
            }
    effectivement , c'est OK !
    Le livre en paramètre de la méthode est l'objet prêt à être sauvé. En refaisant un Find, j'obtiens le livre initial, auquel il faut que j'applique les modifications faites dans la Form .

    Bilan : pour une mise à jour demandée, 2 lectures en base au lieu d'une .
    Ce n'est quand même pas très propre ....
    Est-ce que j'ai compris ta solution?
    Est-ce que tu comprends mon code ?

  7. #7
    Membre confirmé

    Profil pro
    Développeur .NET
    Inscrit en
    Août 2004
    Messages
    178
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2004
    Messages : 178
    Points : 645
    Points
    645
    Par défaut
    J'ai bien compris ton code et tu as bien compris ma solution.

    Effectivement, de cette façon cela occasionne deux lectures.

    Que faire pour une solution plus propre ? Quelques pistes:
    - Gérer un DbContext unique global: ton objet serait ainsi connu, tu ne perdrais pas d'infos, on s'épargne ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // recherche original 
    Livre oOriginal = db.Livres.Find(oModifie.LivreID);
    Reste alors la lecture pour l'auteur.

    Ce qui m'amène au point suivant. Connais-tu le lazy loading et le eager loading ?

    Pour faire bref, lors que tu as des relations avec des entités, comme c'est le cas avec ton livre et ton auteur; en mode lazy, les entités "annexes" ne sont pas chargés par soucis de performances.

    Ce qui explique pourquoi tu as besoin de faire ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     oOriginal.AutLivre = aut;
    Le mode EagerLoading va charger toutes les entités qui dépendent de ton livre, donc l'auteur.

    Je n'ai pas le temps de faire des tests, mais je te conseille la lecture de ce post qui pourrait bien t'aider:
    http://stackoverflow.com/questions/3...ng-as-expected

  8. #8
    Futur Membre du Club
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Si je code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    oOriginal.AutLivre = aut;
    C'est tout simplement parce que l'auteur a été modifié ...

    C'est bien mon problème : Je ne pense pas que ce soit la bonne piste !

Discussions similaires

  1. Réponses: 4
    Dernier message: 02/07/2007, 16h56
  2. Update non effectué
    Par nellynew dans le forum Access
    Réponses: 1
    Dernier message: 13/09/2006, 13h37
  3. Update non fonctionnel
    Par kissmytoe dans le forum Access
    Réponses: 7
    Dernier message: 07/03/2006, 19h37
  4. Update non réussi à cause de la troncature de données
    Par m-mas dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 28/12/2005, 17h18
  5. Réponses: 8
    Dernier message: 13/12/2005, 01h01

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