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 :

Entity framework Migration : utiliser les transactions lors de la migration


Sujet :

Entity Framework

  1. #1
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut Entity framework Migration : utiliser les transactions lors de la migration
    Bonjour à tous.

    J'ai expliqué ce que j'ai fais dans ce post.

    Le problème vient de la fonction InitDatabaseModel :
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    class DatabaseManagerContextApp : DatabaseManagerContext
    {
        /// <summary>
        /// Factory pour créer l'instance de la base de données de type DatabaseManagerContextApp.
        /// </summary>
        public class DatabaseManagerContextFactoryApp : IDatabaseManagerContextFactory
        {
            public DatabaseManagerContext   CreateDatabaseManagerContext()
            {
                return new DatabaseManagerContextApp();
            }
        }
     
        static public void InitDatabaseModel()
        {
            // Analyse et migration de la base de données
            try
            {
                // Initialisation
                Configuration configuration = new Configuration();
                configuration.ContextType = typeof(DatabaseManagerContextApp);
                var migrator = new DbMigrator(configuration);
                // This will update the schema of the DB
                migrator.Update(); // This will run Seed() method
            }
            catch (AutomaticMigrationsDisabledException)
            {   // ici, pas besoin de lancer l'exception, la version de la base de données à changée
            }
            catch (Exception)
            {   // renvoyer l'exception plus haut
                throw;
            }
     
            // Ne pas faire ce code dans le catch (AutomaticMigrationsDisabledException) car si une exception
            // est levée elle ne pourra plus être catchée et il faut le faire au cas où la migration a fonctionnée
            GeneralProvider provider = new GeneralProvider();
            EntityGeneral general  = provider.GetGeneral();
     
            if (general == null)
            {   // Création de la base, ajouter le schéma actuel
                provider.Create(new EntityGeneral { SchemaVersion = App.DATABASE_SCHEMA_VERSION } );
            }
            else
            {   // La version de la BDD doit être <= à cette version de programme
                if (general.SchemaVersion >  App.DATABASE_SCHEMA_VERSION)
                {
                    throw new Exception("Le programme est incompatible avec version de la base de données\n\nVeuillez mettre à jour le programme.");
                }
                else if (general.SchemaVersion <  App.DATABASE_SCHEMA_VERSION)
                {   // Mettre à jour le N° de version de la base de données en concordance avec la version du programme
                    // sachant que la migration a fonctionnée donc la version actuelle du schéma est egal à celui du
                    // programme
                    general.SchemaVersion =  App.DATABASE_SCHEMA_VERSION;
                    provider.Update(general);
                }
            }
        }
     
        /// <summary>
        /// Initialisez un nouvel objet Model.
        /// <param name=_objConnection>Objet connexion.</param>
        /// </summary>
        public DatabaseManagerContextApp()
            : base()
        {
        }
    }
    Il s'avère que cette fonction permet de créer la base si elle n'existe pas, puis de la migrer à la dernière version si elle exite.
    Ça fonctionne très bien.

    Sauf quand ça ne fonctionne pas ! En effet, si un problème survient lors de la migration ou de la création (par exemple, pour ma part un champ string trop long) la base est créée en partie avant l'exception mais comme il n'y a pas de transaction, elle reste tel quel...
    Du coup, lorsque je relance, EF détecte que la base de données n'est pas à jour (forcément puisque la création a en partie échoué au dernier lancement), EF tente de mettre la base de données à jour mais comme les premières tables ont déjà étés créée, une erreur de type 'table déjà existante' est lancée et là je suis bloqué.
    Pour le moment je suis en dev sur une machine de test et le problème est vite réglé : je détruis la base, la recrée et change mon champs pour la taille soit acceptée. Mais une fois en prod, si une erreur que je n'ai pas détectée pendant la phase de dev survient, je risque de gros soucis !
    En encore, si c'est au moment de la création, c'est chiant mais si c'est pendant une migration ça risque d'être bien plus génant.

    Ce que je voudrais c'est créer une transaction autour de cette migration, de sorte que si celà échoue, que l'on puisse rester dans l'état précécent !
    Mais le fait de créer un dbContext avant celui de la migration provoque des erreurs !

    Si vous avez une idée..

  2. #2
    Membre éclairé Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Points : 735
    Points
    735
    Par défaut
    Dans ce cas tu dois utiliser une TransactionScope:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    using(TransactionScope scope = new TransactionScope())
    {
       // Ton code
       scope.Complete(); //  Commiter
    }

  3. #3
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Pour créer une transaction, il faut le faire sur une classe héritée de DbContext.
    Or, le DbContext instancié par dbMigrator est tout simplement innaccessible (pas de fonction dans la classe pour le récupérer).
    De toute façon, lors de l'instruction var migrator = new DbMigrator(configuration); deux contextes sont créés et disposés, puis quand le migrator.Update(); est effectué, aucun nouveau DbContext n'est créé et lorsque l'exception est catché c'est trop tard, les tables ont déjà été créés / modifiées.
    Si je crée un DbContext avant tout le code, là c'est simple, rien ne fonctionne puisque le contexte qui fait la migration doit être le premier.
    Je sais pas comment ça marche en interne mais il y a tellement de couche que lorsqu'on sort du scope on est juste mort !
    Si en prod, lors du lancement de l'appli, ça plante je ne sais pas comment je m'en sortirais, la base sera vérolée et si les tables systèmes de code first ont été mise à jour c'est encore pire... faut prendre le risque et prier....

    C'est assez bizarre qu'il n'y pas de trace de tout ça dans google, parce que une mise à jour du schéma qui merde peut être catastrophique.
    Ah si, j'ai trouvé quelque chose qui pourrait expliquer tout ça !
    En fait, la transaction doit exister dans le code de Microsoft (je suppose s'ils ont bien fait les choses) sauf que j'utilise oracle et d'après ce que je peux lire chez devart (le provider que j'utilise (dotConnect) ) :
    Définition du DDL et Oracle server doesn't support a transactional DDL

    .. ben c'est mort, faut prier alors !

    T'en pense quoi ?

  4. #4
    Membre éclairé Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Points : 735
    Points
    735
    Par défaut
    Tu peux dans ce cas overrider la méthode Seed de la classe configuration, tu crées une nouvelle class Config qui hérite de la classe Configuration, une solution que j'ai trouvé sur ce lien

    Les TransactionScop marchent aussi avec Devart, c'est ce que j'utilise.

  5. #5
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Quand la migration de la BDD plante (ce qui m'arrive), je ne rentre même pas dans la fonction Seed !
    Donc ta solution ne peut pas fonctionner.

    C'est quoi TransactionScope ? J'ai pas cette classe moi !
    Attention je suis en framework 4.0 + EF 6.0 Beta.

  6. #6
    Membre éclairé Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Points : 735
    Points
    735
    Par défaut
    Essaie d'instancier la TransactionScop avant de lancer la migration.

    La classe TransactionScop rend un bloc de code transactionnel, elle se trouve dans le namespace System.Transactions, il faut que tu référence la dll.

    Essaie dans ce cas:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    using(TransactionScope scope = new TransactionScope())
    {
       migrator.Update(); // This will run Seed() method
       scope.Complete(); //  Commiter
    }

  7. #7
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Je n'ai pas cette classe, impossible a tester.
    De plus, je pense qu'il faut au moins lui initialiser une connexion non ?

  8. #8
    Membre éclairé Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Points : 735
    Points
    735
    Par défaut
    Citation Envoyé par Feneck91 Voir le message
    Je n'ai pas cette classe, impossible a tester.
    Tu auras la classe en ajoutant la référence System.Transactions.dll.
    Citation Envoyé par Feneck91 Voir le message
    De plus, je pense qu'il faut au moins lui initialiser une connexion non ?
    - Non il n'y a rien à passer.

  9. #9
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Tu m'as donné un superbe espoir, j'ai tenté.
    J'ai maintenant une erreur :
    local transaction can not be started while in distributed transaction
    Je pense qu'en effet la classe DbMigrator effectue une transaction et ça gène la transaction générale : je pense donc que le code sans la TransactionScope fonctionne mais là, c'est un problème oracle !
    T'en penses quoi ?

    En tout cas merci pour ton aide, même si ça ne donne rien (pour le moment mais ne desespéront pas)

  10. #10
    Membre éclairé Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Points : 735
    Points
    735
    Par défaut
    J'ai rencontré aussi un souci avec l'utilisation des TransactionScope avec Oracle et Devart, daprès Devart et cela a bien corrigé mon souci (qui n'a rien avoir avec le tien mais essayons quand même) il faut remplacer le:

    par:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    new TransactionScope( 
    TransactionScopeOption.Required, 
    new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted })
    Tiens nous au courant.

  11. #11
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Idem, dommage, j'y ai encore cru !
    Même erreur, même punition.
    J'ai tenté de mettre le début du code (using (TransactionScope scope = .....) avant la création de la Configuration puis ensuite, juste avant le migrator.Update();
    Et ça fait pareil ! Grrrrrr

    Je me suis abonné sur leur Forum, des problèmes un peu bloquants sur la génération du nom de la table en mode Many to Many : la nom de la table générée est trop longue et Oracle gueule !
    J'ai mis ça ici.

  12. #12
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    J'ai eu la réponse et leur solution fonctionne.
    Pour le moment, la réponse à la question posée sur ce sujet reste en cours...
    J'ai pas trouvé de réponse...

Discussions similaires

  1. Vs 2012/MVC 4: Entity Framework, comment atteindre les tables?
    Par sundyata37 dans le forum ASP.NET MVC
    Réponses: 3
    Dernier message: 10/09/2013, 10h25
  2. [EJB] Quelles bonnes pratiques pour utiliser les transactions "en ligne"?
    Par kisitomomotene dans le forum Java EE
    Réponses: 1
    Dernier message: 12/12/2011, 20h22
  3. Comment utiliser les transactions avec UIB
    Par zoheir13 dans le forum Connexion aux bases de données
    Réponses: 6
    Dernier message: 13/01/2011, 09h51
  4. Comment bien utiliser les transactions
    Par babacan dans le forum Développement
    Réponses: 4
    Dernier message: 22/06/2009, 08h23
  5. Réponses: 5
    Dernier message: 16/10/2008, 19h14

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