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 :

Enty Framework / Oracle Spacial / Code First Migration : besoin d'aide


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 Enty Framework / Oracle Spacial / Code First Migration : besoin d'aide
    Bonjour à toutes et à tous.

    Entity Framework et Oracle, quelle galère !
    Ça fait bientôt 15 jours que je passe pour créer un squelette d'application avec Entity Framework pour oracle (en code First).

    C'est sur aussi, je pars avec des handicaps, des choix techniques décidé par mes supérieurs qui sont d'ailleurs je pense des bonnes solutions mais plus complexes à mettre en oeuvre que ce qu'ils pensaient.

    Petit historique...

    1. Galère pour une première connexion avec la base de données Oracle :
      • 127.0.0.1 au lieu de localhost et ça fonctionne pas
      • Problème avec le nom de l'écouteur
      • Problème de driver
    2. Utilisation de EntityFramework.
    3. Utilisation de notre base de données interne Oracle.
    4. Utilisation d'Oracle en tant que base de données Spatiale.
    5. Puisque base de données Spaciale, EntityFramework utilise .NET Framework 4.5 => Compatibilité uniquement avec SEVEN : inacceptable
      • Utilisation d'EntityFramework 6.0 Beta + .Net Framework 4 (compatible XP)
      • ODP.Net ne supporte les bases de données spaciales qu'avec .Net Framework 4.5 => changement de fournisseur de données
      • Changement de fournisseur : utilisation de dotConnet + la partie spaciale..
    6. Un vrai bordel à configurer le fichier app.config pour que celà fonctionne.
    7. Enfin, première connexion à la base
    8. Enfin, première connexion à la base + création de la BDD automatique avec Code First
    9. Enfin, première connexion à la base + création de la BDD automatique avec Code First avec des colonnes de type DbGeometry
    10. Maintenant que celà fonctionne il faut penser à la suite la modification du modèle avec CodeFirst Migration car actuellement si le modèle change, l'application ne démarre plus (exception).


    Voilà, j'en suis la et je me pose deux questions :
    • Comment faire fonctionner Code First Migration avec dotConnect ?
      En effet, le tutoriel indique
      Dans les options de scaffolding, vous devez sélectionner la classe Customer. Dans la zone Data context class, vous allez utiliser " New data context…>. Renseignez le nom du Data Context " DatabaseMigrationContext ". Validez ensuite sur OK et cliquez enfin sur Ajouter.
      Seulement je ne sais pas comment il arrive à la boite situé juste en dessous et surtout ce qu'est options de scaffolding ?
    • Ce que je ne sais pas c'est comment réagi et comment est géré Code First Migration lorsque plusieurs versions de programmes sont lancées simultanément. Je m'explique :

      1. Création d'une version 1.0 => La base est créée.
      2. Création d'une version 1.1 => La base est migrée. Que se passe-t-il alors si tous les utilisateurs n'ont pas changés d'applications et sont restée en 1.0 ? Le lancement de l'application en 1.0 va migrer le modèle de la base de 1.1 => 1.0 ?
      3. Que se passe-t-il si un programme version 1.0 est lancé et pendant ce temps un autre en 1.1 migre le modèle en 1.1 ? Le programme lancé en 1.0 risque de faire n'iimporte quoi ?


    Pour le (a) j'avais pensé créer une table Générale avec le n° de version du modèle (j'ai l'impression que je refais ce que fait CodeFirst Migration). Si le programme lancé à un modèle < au modèle de la base, il refuse de se lancer car il est obsolète, hélas ça ne résoud pas le (b).

    J'ai aussi besoin d'un message personnalisé dans le SpashScreen (mise à jour du modèle), or à chaque première ouverture de la BDD par Code First dans le programme (première fois seulement), la fonctrion virtuelle protected override void OnModelCreating(DbModelBuilder _modelBuilder) est systématiquement appelée que le modèle de la base soit à jour ou pas. Comment savoir si le modèle est à jour ou pas pour :
    • Afficher un message "Création du modèle de la base de donnée" si le modèle n'a jamais été créé : (est-ce que la table General existe ou pas par exemple)
    • Afficher un message "Mise à jour du modèle de la base de donnée" si le modèle a déjà été créé et qu'il va falloir le mettre à jour.
    • Ne rien afficher si le modèle est à jour.
    • Lancer une exception si le modèle est plus à jour que ce qui est supporté par l'application (application obsolète).

    Pour celà, je dispose d'une instance de DbConnection et il est possible dans la fonction virtuelle d'ouvrir cette connexion manuellement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
                m_InstanceDbConnection.Open();
     
                bool bIsTableExists;
                using (DbCommand cmd = m_InstanceDbConnection.CreateCommand())
                {
                    cmd.CommandText = @"SELECT 1 FROM sys.tables AS T INNER JOIN sys.schemas AS S ON T.schema_id = S.schema_id WHERE S.Name = 'SchemaName' AND T.Name = 'DMGeneral';";
                    int i = cmd.ExecuteNonQuery();
                    bIsTableExists = true;
                }
    Bon, ce code c'est n'importe quoi, je ne sais pas comment on récupère la présence ou pas d'une table de la base de données en Oracle mais c'est un exemple de ce que l'on peut faire.
    Il me faut deux choses :
    1. Détecter la présence (ou pas) de la table générale.
    2. Si la table générale existe, détecter le n° de version du schéma dans cette table.


    Voilà, désolé pour ce message un peu long mais je voulais expliquer un peu le contexte de tout ça.
    S'il y a des pros de Oracle et de Code First / Code First Migration qui peuvent m'aiguiller ça serait cool.
    Et puis une petite requête SQL pour vérifier la présence d'une table à partir d'un DbConnection ça serait cool.

    Merci à tous !

  2. #2
    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
    Bon, ceci n'est pas un 'up', je vais donner quelques réponses à mes questions...
    Mon projet est composée de 3 briques :
    L'application (IHM) / Le DAL et le DTO comme indiqué au début du tutoriel ici

    Comment faire fonctionner Code First Migration avec dotConnect ?
    Bon pour tout, il faut utiliser la console du gestionnaire de package. (Menu Outils > Gestionnaire de package de bibliothèques > Console du gestion de package).
    Il faut d'abord ajouter la migration avec la commande Enable-Migrations
    Seulement voilà, pour ma part ça ne fonctionnait pas (il y avait une erreur). Pourquoi ?
    Parce que ma classe héritée de DbContext ne se suffit pas à elle même. Un singleton DbConnection est utilisé pour gérer la connexion, celui-ci étant initialisé par la classe dérivée (dans le projet App) et il n'y a pas la connexion string dans le app.config.
    Il y les informations dans les settings récupérées au runtime par le programme. Or la commande Enable-Migrations instancie en fait votre classe de contexe pour accéder à la base et du coup ça ne fonctionnait pas (il faut aussi que votre projet compile bien entendu).
    Comment j'ai fais ? J'ai hardcodé en mode debug le string de connexion comme cela :
    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
            /// <summary>
            /// Récupérer l'instance de connexion.
            /// 
            /// Permet de se connecter manuellement à la base de données.
            /// </summary>
            /// <returns>Un instance de connexion</returns>
            public static DbConnection                      Connection
            { 
                get
                {
                    if (m_InstanceDbConnection == null)
                    {
    #if DEBUG
                        // !! Attention : TRES IMPORTANT !!
                        // Pour faire fonctionner Entity Framework Migration, Le gestionnaire de package utilise le
                        // code source pour détecter les changements en base de données. Pour cela, il instancie la classe de
                        // contexte qui doit donc OBLIGATOIREMENT pouvoir se connecter lorsqu'elle est instancié sans aucune autre
                        // initialisation. Ce code ne sert donc qu'à celà et il doit contenir toute la chaine de connexion
                        // necessaire pour se connecter à la base de données de développement.
                        m_InstanceDbConnection = new OracleConnection("User Id=userxxx; Password=mdp; Data Source=localhost:1521/myListener;");
    #else
                        throw new Exception("Instance de connexion non initialisée !");
    #endif
                    }
                    return m_InstanceDbConnection;
                }
                set
                {
                    m_InstanceDbConnection = value;
                }
            }
    Ceci crée un fichier Configuration.cs dans la section Migrations. Dans le constructeur il doit y avoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    AutomaticMigrationsEnabled = false;
    Pour le point a
    Il faut exécuter la commande Add-Migration InitialCreate (ou un autre nom) avec une base de données vide. Ceci crée un fichier dans Migrations où toute la base de données est créée.

    Pour le point b
    Il faut exécuter la commande Add-Migration v2 (ou un autre nom) avec une base de données correspondant à l'état initial. Ceci crée un autre fichier dans Migrations où toute la base de données est migré de InitialCreate à v2.

    Voilà dans la théorie ça marche mais en pratique non. Pourquoi ? Parce que Code first migration ne fonctionne qu'en mode migration, si la base n'existe pas ça ne fonctionne pas, or je travaille sur une machine de dev avec une base de données fictive, je ne vais pas travailler sur la base de production. J'ai besoin en phase de dev de créer depuis rien, puis de migrer (dans les différentes étapes de dev j'aurais certainement des modifications du modèle de la BDD)... quand tout est ok, je détruit les fichiers de migrations et je les re-créé avec mon schéma de base de données final. Ça ça m'a vraiment ennervé.
    Plein de choses ne fontionnent pas :
    • L'appel de la fonction moncontext.Database.CreateIfNotExists(); (rien n'est effectué)
    • L'appel de la fonction moncontext.Database.Exists(); Cela fonctionne bien mais une fois le contexte utilisé il ne peut plus être utilisé à nouveau et le deuxième contexte utilisé ne peut plus créer la base : seul le premier contexte créé peut créer / migrer la base (même en appelant la fonction Initialize du Database). Pourquoi ? Je ne sais pas, d'ailleurs la fonction protected override void OnModelCreating(DbModelBuilder _modelBuilder) n'est appelé que la première fois.

    Comment faire alors ?
    J'ai créé une méthode statique dans la classe de mon contexte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            static public void InitDatabaseModel()
            {
                Configuration configuration = new Configuration();
                configuration.ContextType = typeof(MyContext);
                var migrator = new DbMigrator(configuration);
                // This will update the schema of the DB
                migrator.Update(); // This will run Seed() method
            }
    Ce code permet de créer la base si elle n'existe pas et de la migrer si elle existe.
    On peut le voir car la méthode protected override void Seed(DataManager.MyContextApp _dbContext) de la classe migration est bien appelé ainsi que la méthode public override void Up() de chaque classe de migration (dérivée de la classe DbMigration).
    Lorsqu'une base est créé, la création commence par la plus ancienne version (fonction Up de la classe InitialCreate) puis appel chaque fonction Up de chaque classe de migration jusqu'à la dernière version.
    Voilà, enfin ça marche !

    Pour le point c
    Je n'ai pas encore de réponse.

    Pour les points a et b
    J'ai quelque peu modifié 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
            static public void InitDatabaseModel()
            {
                try
                {
                    Configuration configuration = new Configuration();
                    configuration.ContextType = typeof(MyContext);
                    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();
                EntityDMGeneral 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);
                    }
                }
            }
    Voilà c'est un peu long mais si ça peut aider les gens...

    Du coup j'ai une nouvelle question :
    Je vais importer des tables existentes d'une base de données existante dans ma base de développement et j'aurais besoin de faire le lien entre les tables de la BDD et mon code, sans pour autant vider ces tables.
    J'espère qu'il n'y aura pas de problème car dans mes tables actuelles, j'ai des foreigner keys sur des ID de tables existente qu'il va falloir binder...

  3. #3
    Futur Membre du Club
    Inscrit en
    Novembre 2010
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2010
    Messages : 5
    Points : 5
    Points
    5
    Par défaut
    Bonjour,

    Ce que tu essaies de faire avec la méthode statique (InitDatabaseModel) se fait en principe avec :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
    Non ?

    Pour le (c) : Franchement je ne vois pas trop comment tu pourrais faire ça ! A part le faire à la mano et des checks partout ! Des objets sérialisés avec différentes versions en base, ok ! Mais la gestion de version de toute une base, franchement jamais ! Y a un réel besoin pour ça ?!

    Pour la requête essaye peut être ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var blogs = context.Blogs.SqlQuery("SELECT * FROM dbo.Blogs").ToList();
    ou sinon

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    context.Database.SqlCommand(
            "UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");

    avec une requête pour vérifier si la table existe bien sûr genre (je ne sais pas si c'est bien cette requête pour Oracle) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT COUNT(*)
        INTO l_cnt
        FROM dba_tables
       WHERE owner = <<table owner>>
         AND table_name = <<table name>>;

    Cordialement,

  4. #4
    Futur Membre du Club
    Inscrit en
    Novembre 2010
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Novembre 2010
    Messages : 5
    Points : 5
    Points
    5
    Par défaut
    J'ai peut être parlé trop vite !

    http://msdn.microsoft.com/en-us/data/jj591621.aspx

    Multiple Models Targeting the Same Database

    When using versions prior to EF6, only one Code First model could be used to generate/manage the schema of a database. This is the result of a single __MigrationsHistory table per database with no way to identify which entries belong to which model.

    Starting with EF6, the Configuration class includes a ContextKey property. This acts as a unique identifier for each Code First model. A corresponding column in the __MigrationsHistory table allows entries from multiple models to share the table. By default, this property is set to the fully qualified name of your context.
    En gros plusieurs modèles peuvent utiliser la même base ! Il suffit juste de changer le contexte !

    Essaye peut-être ça ?!

Discussions similaires

  1. Entity Framework : à la découverte de Code First Migrations
    Par Hinault Romaric dans le forum Entity Framework
    Réponses: 3
    Dernier message: 06/01/2016, 15h48
  2. Entity Framework : l'approche Code First
    Par Reward dans le forum Contribuez
    Réponses: 4
    Dernier message: 13/04/2014, 17h06

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