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 :
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
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 :
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 :
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...
Partager