menu
  Home  ==>  articles  ==>  bdd  ==>  migration_bde   

Migration BDE - John COLIBRI.




1 - Migration BDE

Cet article présente un bilan de plusieurs migrations visant à supprimer le BDE d'une application DELPHI.

Ces applications ont été de tailles modestes (50 unités, 30.OOO lignes de code) à des familles de produits plus imposantes (2.000 unités, 1.500.000 lignes de code), en utilisant des moteurs tels que Paradox, Interbase, Sql Serveur.

Nous allons présenter:

  • un bilan sur le BDE et les raisons qui poussent à effectuer une migration du BDE vers d'autres jeux de composants
  • les autres moteurs et jeux de composants, évalués dans la perspective d'une migration depuis le BDE
  • la migration des données
  • la migration SQL
  • la migration du code
  • le bilan en durée et coût
Paradox et dBase n'étant pratiquement supportés que par le BDE (Odbc ou Ado à la rigueur), nous supposons aussi que la migration a pour but l'utilisation de moteurs Sql.




2 - Avantages et Inconvénients du BDE

2.1 - Architecture BDE

Le BDE a fait la fortune de Delphi: en quelques clics, un composant d'accès était posé sur une Forme, associé à quelques composants de visualisation et quelques validation.



2.2 - Avantages du BDE

Parmi les avantages des tTable, tQuery, tStoredProc, tBatchmove, citons :
  • possibilité de travailler en mode Desktop, avec des fichiers locaux (Paradox, dBase, Access) ou avec de véritables moteurs Sql (Oracle, Sql Server, Interbase)
  • transparence tTable / tQuery
    • possibilité d'utiliser des tQuery avec des moteurs qui privilégient l'accès fiche à fiche (dBase)
    • possibilité d'utiliser de tTable avec des moteurs qui ne comprennent que le Sql (Paradox, Oracle, Sql Server, Interbase). Ce mode évitait l'apprentissage du langage Sql, le BDE se chargeant de la traduction en Sql des instructions sur le tTable (tTable.Insert étant traduit en INSERT INTO etc)

  • quelques facilités de la syntaxe Sql
  • composant tBatchMove permettant des copies, ajout entre tables (du même ou d'un autre type de moteur Sql)
  • possibilité de mélanger sans état d'âme les instruction de modification du dictionnaire (DDL : CREATE TABLE via tTable.CreateTable, par exemple) et de modification de données (DML : UPDATE / INSERT INTO / DELETE)
  • gestion automatique des transactions
  • tamponnage des données permettant de charger les lignes par paquets successifs (fetch), évitant à la fois l'attente de la fin du chargement et limitant la taille mémoire nécessaire (mécanisme de curseur)

  • utilisation par défaut de transactions implicites
  • mode multi utilisateur avec verrouillage pessimiste (accès exclusif à la ligne pendant une modification)


2.3 - Inconvénient du BDE

  • pour les utilisateurs de dBase, Paradox, gestion directe des fichiers (pour lister les tables, effacer / copier / renommer des tables ...), ce qui ne correspond pas à des instructions Sql
  • de façon similaire, utilisations de répertoires différents, correspondant à des bases différentes
  • par défaut, pas de maîtrise des transactions
  • pas de maîtrise du Sql généré lors des modifications de données par un tTable (clause WHERE)
  • pas de maîtrise des tampons (taille de chaque "fetch")
  • quelques erreurs, d'ailleurs, dans la gestion des tampons (erreurs lors de l'execution d'une application depuis l'IDE, obligeant à recharger l'IDE)
  • évolution stoppée en 2001 (avec le développement de Kylix). Delphi ne souhaitant pas porter le BDE sous Linux, Midas / DataSnap / Dbx ont été développés.
    • le portage a toujours été effectué (le BDE fonctionne encore avec Delphi Xe3, en 2013)
    • mais aucune évolution ou correction de bugs n'ont été effectués
    • certains de nos clients ont rencontré des impossibilités de transmettre via le BDE des instructions Transact Sql (Sql Serveur) complexes

  • craintes de problèmes à venir avec les futures évolutions de Windows (nous avons entendu parler de problèmes avec Windows 7 mais n'avons pas de détails précis)
  • l'évolution inéluctable vers le 64 bit pourrait aussi être une cause de migration
  • évolution no prévues sur les autre plateformes (Mac, Linux, mobiles)
  • les tTable ont toute une série de propriétés / méthodes qui ne sont pas toujours directement traduisibles en Sql (le BDE se chargeant de la génération de Sql pour chaque cas). Citons, entre autres 
    • IndexFieldName, IndexName
    • CreateTable, CreateIndex, Delete
    • GotoNext, GotoNearest, FindField
    • accès exclusif à une table
    • SetRange

  • certaines primitives bas niveau du BDE (dbi_RegenIndex pour régénérer les indexes dBase, par exemple) nécessitent des traitements particuliers
  • les transactions implicites (une transaction par écriture), si nous les utilisons, peuvent générer un traffic réseau important
  • manque de réactivité du verrouillage pessimiste en cas de nombreuses modifications par de multiples utilisateurs


Les composants les plus utilisés (ils étaient apparus quelques semaines après le lancement de Delphi) sont les composants InfoPower, tels que le tWwTable (WW pour Woll2Woll, le nom de Roy WOLL), et surtout la tWwDbGrid qui était jadis "la" grille utilisée pour améliorer la tDbGrid.

Du point de vue de la migration, ces composants se distinguent par

  • une utilisation des primitives de bas niveau du BDE (appels dbi_xxx)
  • des propriétés assez exotiques, plus ou moins calquées sur Paradox (tWwQbe, PictureMasks)


Quant aux composants maison, leur complexité dépend de leurs auteurs. Mentionnons simplement qu'ils devront être examinés au même titre que les composants BDE qu'ils utilisent



2.4 - Lente dérive des applications (code rot)

Les applications démarrées avec le BDE fin 1990 souffrent souvent d'architectures mal adaptées à une évolution progressive à travers les années :
  • le modèle "forme / tTable / dgGrid" qui était valable pour 15 fenêtres et 10 tables ne peut plus être utilisé pour 100500 fenêtres et 100 /200 tables

  • le défaut essentiel est le mélange "contrôles visuel / règles métier / gestion des données"

    L'archétype de ce type de mélange des genre est :

    Table1.FieldByName('invoice_total').AsFloat:= Max( 100.00, StrToFloat(Edit1.Text )) ; 

    où nous mélangeons

    • les données (Table1.FieldByName)
    • le contrôles visuels (Edit1)
    • les règles métier (le minimum de facturation est 100)

    Ce mélange est coûteux car
    • il interdit toute migration vers d'autres plateformes (FireMonkey, mobiles).

      Les interfaces visuelles n'ont pas arrêté de changer

      • mode DOS ReadLn, Writeln
      • Windows et les événements: Edit1.Text, OnChange etc
      • bientôt les gestes et les écrans tactiles, la reconnaissance vocale etc

      Le code algorithmique pur (des calculs statistiques, des inversions de matrices) peut être migré quasiment depuis l'Apple ][ jusqu'à Android sans aucun souci)

      Il est éventuellement souhaitable de déporter les règles métier vers un moteur Sql (triggers, tStoredproc)

      Ou d'utiliser des organisations plus distribuées

      • multi-tiers
      • services Web ou autre types de services
      • interface Web, mobiles
      • séparation front end (Windows, Web) éventuellement les deux, et back end (règles métier, gestion des données)

    • il rend tout test coûteux: nous devons écrire des scripts de plus en plus complexes, peu lisibles, difficiles à maintenir et faire évoluer, pour tester, via l'interface graphique, le bon fonctionnement des règles métier ou des instructions de gestion des données.

      Or, "test difficile" rime quasiment toujours avec "pas de test"

    • difficultés à remplacer des composants datant de 1995 par des composants plus récents (par exemple grilles InfoPower -> grilles TMS ou Developper Express)

    • naturellement, la documentation est en général du même niveau


Pour faire bon poids, nous pouvons ajouter les défauts d'architecture :
  • Form1 de départ a peu a peu été agrémenté de Constantes, voire de Classes, et les Formes et unités ajoutées à travers les ages, références (dans l'Implementation cette Form1 pour accéder à ces paramètres. Naturellement, il faudrait stocker ces paramètres / méthodes / Classes dans des unités appelées par Form1 et les autres formes et unités. Ce défaut peut se retrouver avec différentes variantes à divers niveaux de l'architecture.

    En gros des unités fortement couplées au lieu d'un diagramme acyclique. Avec un problème évident pour réutiliser les unités de base si l'application se transforme en "famille d'applications". Défaut de factorisation, duplication de code, incohérence entre les différentes versions des traitements similaires etc

    Ce défaut n'est naturellement pas lié au BDE. Mais nous l'avons retrouvé dans plusieurs applications créées avant 2000. En général les développeurs en charge de ces applications sont très conscients du problème, mais souvent sous la pression de fournir des fonctionnalités nouvelles en vue de gagner des parts de marché.

  • évolution difficile de l'ergonomie de l'application, provoquée par un manque de conceptualisation initial

  • absence de gestion objet. Certes, l'utilisation d'objets métier n'est pas une nécessité, mais elle améliore la maintenance, l'évolution et la testabilité. Sachant que l'essentiel du coût d'une application se situe dans sa maintenance (l'application sur laquelle vous travaillez actuellement a été créée quand ?), le bénéfice est évident.


2.5 - Pourquoi cette migration est-elle redoutée

La raison essentielle pour laquelle une migration BDE est souvent redoutée est le côté tout ou rien. Pour une petite application de l'ordre d'une centaine de formes / unités, il faudra remplacer les composants BDE et leur propriété en une seule fois avant de pouvoir arriver à compiler le projet et tester le bon fonctionnement des remplacements. Et si, après avoir fini par faire fonctionner la nouvelle version vous rencontrez des problèmes (performance, fonctionnement en multiposte, avec des transactions etc) et que vous décidiez d'utiliser d'autres composants de remplacement, il faut pratiquement tout reprendre depuis le départ.

Travail fastidieux, dont le résultat n'apparaît qu'à la fin et les choix ne peuvent être évalués qu'après une migration complète.

Nous verrons plus loin que l'utilisation d'outils automatiques (permettant donc plusieurs types de transformation à partir des mêmes sources) résoud ce problème de façon radicale.



D'autre raisons peuvent aussi repousser la décision d'une telle migration:

  • le code est archaique (mélange Forme / tDataset etc)
  • les développeurs initiaux ne sont plus disponibles, ou même s'ils sont encore présent, ils n'ont plus la mémoire de ce code (commentaires inexistants, documentation rare etc)
  • la suppression BDE devrait aussi permettre une évolution vers d'autres composants visuels (grille TMS / DevExpress, Ribbon Controls), l'architecture devrait être modifiée (MVC, multi-tiers, interface Web) etc.



3 - Choix des composants pour remplacer le BDE

3.1 - Résumé des possibilités

Voici une image qui résume la situation :

archtecture

et

  • en haut les composants visuels (tEdit, tdbEdit, tdbGrid)
  • au milieu les composants d'accès Delphi
    • tDataSource, tDataSet et ses descendants (tTable, tIbDataSet, tOraQuery), composants de connection (tAdoConnection, tIbDatabase), de requêtage direct (tIbSql, tAdoCommand), de gestion des transactions (tIbTransaction)
    • les couches d'accès système avec les pilotes (BDE, Ibx, Dbx, Ado, Doa et autres Oda, DAC for Mysql, Zeos etc)

  • le moteur Sql
    • les composants clients (TNS Oracle, Ib Client etc)
    • le serveur et les données (Oracle, Sql Serveur, Interbase, FireBird, Mysql etc)


Par rapport au BDE, nous constatons
  • une couche de connexion, remplaçant les alias du Bde (ou similaires au tDataSet)
  • des composants d'accès aux données comportant souvent
    • le composant principal d'accès aux données
    • des composants de requêtage non tamponnés (tIbSql, tAdoCommand)
    • des composants de transaction, souvent associés ou nichés dans le composant de connexion
    • des version tTable ou tQuery ayant pour seule vocation de permettre une migration plus aisée depuis le BDE (tIbTable, tAdoQuery), souvent descendants du composant principal d'accès aux données (tAdoTable dérive de tAdoCustomDataSet)

  • des pilotes souvent séparés de la couche générique d'accès (Odbc, Dbx)


Les composants d'accès se divisent en deux catégories
  • les composants spécifiques à un moteur de base de données (Ibx, Oda, Doa etc)
  • les composants génériques utilisant des pilotes spécialisés par moteur (Dbx, Ado, Zeos)


Nous pourrions aussi mentionner les couches de persistence objet (il y en a actuellement plus d'une dizaine, dont tIopf, feu Eco, Instant Object, dSharp, notre Mormot national etc). Ces mécanismes d'accès ne sont pas le sujet central de cet article. Il est d'ailleurs assez rare qu'une société passe directement de tTables BDE aux mécanismes de persistence objet. Nous n'évoquerons pas non plus la migration vers des composants non standard Delphi (comme les objets "data abstract" de RemObject ou les composants utilisant leurs propres mécanismes d'accès aux données (DevX).



Finalement quasiment tous les moteurs Sql sont accessibles.

  • depuis le plus cher: Oracle
  • intermédiaire : Sql Serveur
  • peu cher : Interbase
  • et les gratuits : Firebird (la base la plus utilisée semble-t-il avec Delphi), MySql, PostGres


3.2 - Choix du moteur

Cet article n'a pas pour but de militer pour tel moteur plutôt que tel autre



3.3 - Choix des composants d'accès

Nous n'essayerons pas non plus de mesurer les avantages / inconvénients des différentes couches d'accès. Les problèmes de migration sont pratiquement les mêmes de toutes les façons



Néanmoins nous excluons à priori l'utilisation de composants t_xxx_Table (tIbTable, tAdoTable)

  • comme nous l'avons exposé plus haut, ces composants sont une adaptation des composants génériques souvent adaptés, plus ou moins bien, pour avoir à peu près les mêmes fonctionnalités et interfaces que les tTable et tQuery
  • ces adaptations sont souvent incomplètes, et quelque fois buggées.
Dans ce cas particulier, il faudra donc ajouter l'écriture de requêtes Sql.

Les t_xxx_Query ne sont en général pas mieux lotis. Par exemple le tIbQuery a besoin d'un tIbUpdateSql pour arriver à modifier les données provenant de composants visuels.



Certes, nombreux sont les articles dressant une savante table de correspondance tTable -> tAdoTable, ou tQuery -> tIbQuery. Naturellement qu'un simple remplacement dans les .PAS et les .DFM sera facilité. C'est par définition la vocation de ces composants d'adaptation. Mais en choisissant cette route, vous vous privez de l'essentiel des avantages d'une migration hors du BDE: la possibilité d'écrire les requêtes Sql correspondant le mieux au seul langage que comprennent les moteurs Sql: le Sql.

Mentionnons aussi que certaines sociétés proposent d'autre composants d'accès aux bases de données, et fournissent éventuellement un outil de migration. Nous ne les avons pas testés, et même certains de nos clients avaient déjà commencé à utiliser des composants ADO ou Ibx et ne souhaitaient pas utiliser de composants tiers.



Nous ne nous intéresserons donc qu'aux composants génériques de chaque jeu de composant. Les tSqlDataSet, tIbDataSet, tAdoDataSet etc.



Donc, quel que soit le choix, trois problèmes se poseront :

  • la migration éventuelle des données (par exemple de Paradox vers Oracle)
  • l'écriture de requêtes Sql (si vous passez de composants tTable à des composants Sql)
  • le remplacement et les adaptations du code .PAS et .DFM pour les composants choisis



4 - Migration des données

Si vous utilisez actuellement des moteurs DeskTop (dBase, Paradox, Access), vous aurez éventuellement à migrer les fichiers de données vers les données associées au moteur Sql que vous avez choisi.

Ces bases sont aussi accessibles par Ado (ConnectionStrings.Com vous fournira les ConnectionString), mais en général la suppression du BDE s'accompagne aussi de la suppression de ces moteurs Desktop.



4.1 - DataPump

Delphi a fournit a travers les ages divers mécanismes permettant la migration des données entre divers moteurs
  • le BDE avait le composant tBatchMove
  • divers "datapumps" ont été proposés
Nous vous enjoignons à vous reporter vers la documentation de ces outils



4.2 - Migration par code

Nous avons pour notre part toujours migré les données par des petits utilitaires Delphi. Ceci nous permet de
  • modifier les noms éventuels de colonnes, tables, indexes, contraintes etc
  • mieux contrôler les types de champs utilisés par la base cible


4.2.1 - Création du schéma : les noms

Pour les noms de symboles Sql, mentionnons
  • la suppression systématique (un choix personnel) de tout caractère accentué, espace ou caractère autre que ceux acceptés pour les identificateur Pascal (ce qui évitera l'utilisation de caractères d'échappement tels que " ou les anti-quote style MySql)
  • le choix minuscule / majuscule qui peut dépendre du moteur cible (avec Ibx, même si nous utilisons en Delphi des noms minuscule, seuls les majuscules permettront semble-t-il d'accéder aux tables, même si nous passons en dialecte 3)
Vous pouvez, naturellement, ne pa souscrire à ces choix, qui nécessiteront éventuellement de modifier les parties du code (.PAS, .DFM, procédures cataloguées) correspondantes.



4.2.2 - Les types de données

Pour ce type de transfert, il faut
  • dresser les types de données utilisés par les tables BDE
  • examiner les types de données du moteur Sql cible
Certaines adaptations des types de données sont nécessaires. Par exemple
  • Boolean n'existe pas dans tous les moteurs Sql
  • les moteurs Sql offrent en général des CHAR et des VARCHAR. Ces choix peuvent nécessiter éventuellement un formatage à la taille ou au contraire un Trim lors du transfert des données
  • pour les chaînes il convient aussi de vérifier quel type d'encodage est disponible sur le moteur cible (Unicode ou autre)
  • les valeurs numériques ont des représentations très diverses suivant les moteurs Sql. Vaut-il mieux utiliser un NUM(17,2) ou un FLOAT / DOUBLE ? Naturellement nos pouvons toujours utiliser type générique Sql 92, mais un choix plus précis peut être nécessaire en fonction de l'application et surtout de ce qu'offre le moteur Sql cible
  • finalement il existe différents types de date. En particulier les TIMESTAMP n'ont pas la même sémantique (en MySql, le TIMESTAMP d'une ligne modifiée prend la date de la modification, en Firebird, c'est simplement un identificateur unique de date)
  • les Blobs nécessitent un examen approfondi
Moralité : passez une ou deux heures dans le chapitre "types de données" de votre moteur Sql cible avant de dresser la table de correspondance des types



4.2.3 - Création du schéma : les Index

Certains moteurs Sql ont des traitements particuliers pour les index :
  • Paradox impose par exemple que tout les tables ont un Index, qui est la première colonne, et qui n'a pas de nom
  • dBase permet de créer des index conditionnels (les 2 premier caractères du code postal plus, en majuscule, les 3 premiers caractères du nom de famille etc)


4.2.4 - Création du schéma : les Contraintes

Paradox permet aussi de définir un certain nombre de contraintes, comme des contraintes d'intégrité référentiel, des règles de validation de modification de valeurs de champs etc.

Ces règles doivent naturellement être traduites dans les contraintes du schéma de la base Sql cible.

Le plus difficile pour nous a été de pouvoir analyser ces contraintes qui ne sont pas accessibles par les propriétés simples des tDataSets



4.2.5 - CREATE TABLE, INDEX, CHECK, DOMAIN etc

Une fois que nous pouvons analyser le schéma de l'ancienne base correctement, nous pouvons créer les instructions Sql pour créer le schéma utilisant le nouveau moteur Sql et transférer les données.

Il est possible

  • soit générer les requêtes sous forme de script Sql ou de Const Delphi et injecter ces requêtes dans un autre projet qui se charge de la création du schéma et du transfert des données
  • soit de générer les requêtes Sql et les utiliser directement pour ces traitements


4.2.6 - Services

Nous avons alors toutes la gammes des services possibles
  • nous charger de la création de la nouvelle base
  • fournir un .EXE pour permettre au client de faire cette création lui-même
  • proposer le source de cette migration de données
La dernière solution est surtout retenue par les clients qui ont déjà une base d'utilisateurs qui doivent pouvoir migrer leurs données vers la nouvelle base. Avec nos sources, ils peuvent incorporer le traitement dans leur offre, ou présenter un outil ayant la même présentation que les autres produites de leur gamme.




5 - Migration du Code

C'est là, naturellement l'essentiel de l'effort. Il faudra changer
  • les unités, les composant et les propriétés de ceux-ci
  • dans les fichiers .PAS et les .DFM (éventuellement .DPR)


5.1 - Etat des lieux

Si possible, la première chose est de dresser une statistique du volume et de la complexité des modifications à apporter.

Un simple "Find in Files" pour les composants de base de BDE peut déjà donner une idée (combien d'unités sont touchées, combien d'occurences). Des utilitaires comme GREP peuvent aussi vous aider à ce niveau.



Nous utilisons pour notre part un outil un peu plus précis qui

  • permet de définir les types de données à trouver (tTable, tQuery... ou les composants dérivés ou utilisant le BDE, comme les tWwTable, tWwDbGrid d'InfoPower, ou encore des composants "maison")
  • fournit un tableau à deux dimension des utilisation de chaque type de composant par unité


Voici un exemple de tableau (sur une centaine d'unités que nous avait fait parvenir un client pour évaluer la charge de travail d'une migration):


40_bde_related_items_per_unit



5.2 - Le choix des composants de remplacement

5.2.1 - Différence des hiérarchies

La première étape est de prendre connaissance des hiérarchies respectives au niveau des composants disponibles, et de leurs caractéristiques prorpres comme remplaçants potentiels de composants BDE



5.2.2 - Hiérarchie BDE

Voici pour commencer la hiérarchie BDE:

components_with_ww_4



Notez que

  • nous avons ajouté les composants InfoPower (t_www_xxx), que nous avions rencontrés comme "composants à migrer" dans une de nos missions


5.2.3 - Hiérarchie des descendants BDE

Il faut aussi effectuer le même travail pour les composants dérivés ou utilisant le BDE
  • comme les composants tWw_xxx (que nous avons présenté ci-dessus et figuraient dans notre tableau de recherche de composants à modifier)
  • les composants "maison" (tGa_xxx dans notre exemple ci-dessus)


5.2.4 - Hiérarchie ADO

Au niveau ADO, par exemple la situation, est la suivante :

ado_uml_class_diagram_2



Si vous choisissez ADO pour remplacer le BDE, il est important de passer quelques heures à vous familiariser avec ce jeu de composants si vous ne l'avez jamais utilisé.

Parmi les caractéristiques ADO, mentionnons

  • comme indiqué auparavant, nous déconseillons l'utilisation de tAdoTable, tAdoQuery, tAdoStoredProc, qui ne sont que des adaptations de tAdoCustomDataSet pour "faciliter" une éventuelle migration BDE
  • nous recommandons donc tAdoConnection tAdoDataSet et tAdoCommand
  • tAdoDataSet a 3 modes permettant d'effectuer des traitements en mode Sql, Table (équivalent à un tTable) et StoredProc. Donc aucun regret à utiliser ce composant à la place de leurs trois équivalents BDE
  • ces composants ont de nombreuses possibilités inconnue du BDE, comme la position du curseur, le type de curseur, le mode d'exécution et le type de verrouillage. Ils ont aussi un mode unidirectionnel et lecture seule
  • comme ces composants implémentent une ou plusieurs Interfaces Ado, ils permettent aussi d'appeler directement les méthodes Ado et gérer le RecordSet


Pour la migration BDE, les points suivants sont importants:
  • il n'existe pas de composant équivalent à tUpdateSQL ou tBatchMove
  • les paramètres seront le principal problème si vous migrez des requêtes paramétrées ou des procédures stockées avec paramètres. Ainsi
    • Parms devra être remplacé par Parameters
    • ParamByName('xxx') sera remplacé par Parameters.ParamValue['xxx']
    • les valeurs lues ou modifiées sont des Variants. Il faudra alors supprimer les As_xxx, utiliser Value, modifier le traitement des valeurs NULL, et certaines conversions etc

  • au niveau performance, le principal point à surveiller est le chargement en mémoire de toutes les lignes d'une requête si le curseur est local. Il conviendra donc de s'assurer très tôt des techniques à utiliser pour éviter que la performance ne chute exagérément


5.2.5 - Hiérarchie Ibx

ibx_uml_class_diagram_2



Pour les spécificités Interbase / Firebird, mentionnons

  • les transactions sont au coeur du fonctionnement de Firebird (système de Versioning). Donc
    • il n'est pas impératif de comprendre ce fonctionnement dans le détail, mais soyez assurés que tôt ou tard le sujet reviendra sur le tapis
    • beaucoup de développeurs n'utilisent qu'une seule transaction par application, et ne touchent jamais à Commit ou Rollback. Notre expérience est que pour la modification du schéma, cela se révèle indispensable
    • Commit fermant les tDataSets, il peut être utile d'employer CommitRetaining
    • les modes d'isolation des transactions peut avoir aussi un effet sur la performance de vos applications

  • tIbTable a la réputation d'avoir quelques limitations. Donc mieux vaut ne pas l'utiliser. tIbQuery nécessite l'association avec un tibQuery pour les modifications
  • en résumé nous favorisons tIbSql pour les lectures / écritures non tamponnés, tibDataSet pour les accès usuels tamponnée, et tIbStoredProc pour les procédures cataloguées
  • les composants Ibx possèdent de nombreuses fonctionnalités inconnues du BDE (moniteur, extraction de script qui ont fait l'objet de plusieurs articles sur ce site). Ces fonctionnalités plaident en faveur de Firebird, mais n'interviennent naturellement pas dans le processus de migration


Au niveau des points à surveiller pour la migration :
  • le mélange d'instructions DDL et DML (un CREATE TABLE et un UPDATE TABLE) ne font pas toujours bon ménage lorsqu'elles sont imbriquées entre elles
  • la fermeture des tDataSet par les transactions peut aussi être la source de bugs un peu difficile à localiser (Fields.Count à 0, par exemple)


5.2.6 - Hiérarchie Dbx / DataSnap

dbx_datasnap_uml_class_diagram



Notez que

  • nous n'avons pas représenté les composants de connection multi-tiers (tDcomConnection etc)
  • nous nous sommes limités aux composants pré d2009 (pas de méthodes serveur etc)


5.3 - Composants dérivés du BDE

Lors d'une migration pour supprimer le BDE il faut aussi tenir compte de composants dérivés du BDE
  • les composants commerciaux
  • les composants "maison"


5.4 - Delta

La décision la plus importante est celle du jeu de composants de remplacement.

Plusieurs décisions

  • la famille des composants (ADO, Ibx, DataSnap etc)
  • pour chaque composant BDE utilisé, quel remplacement lui affecter (tTable vers tAdoTable ou vers tAdoDataSet ?)
Nous ne pouvons que répéter nos conseils:
  • il est important d'avoir une bonne idée des propriétés des jeux de composants cible
  • une fois un choix arrêté, vérifier
    • la charge de la migration
    • la performance sur quelques exemples représentatifs


Pour la charge de travail de la migration, trois étapes
  • dresser la liste différentielle des propriétés / méthodes / événements entre chaque composant BDE et le composant sélectionné pour le remplacer
  • vérifier dans les applications à migrer quelles propriétés du BDE n'existe pas dans les composants cibles et déterminer comment remplacer les fonctionnalités manquantes
  • vérifier que, même pour les propriétés présentes des deux côtés ont le même fonctionnement


5.4.1 - Liste différentielle des propriétés

Prenons le cas d'un remplacement d'un tTable par un tIbDataSet. Les propriétés de tTable que tIbDataSet n'auront pas à être réimplémentées (à des différences sémantiques près). Par exemple, toutes les propriétés de base d'un tDataSet fonctionneront sans modification (Open, Close, Post, Next etc).

En revanche les propriétés du tTable n'existant pas dans le tIbDataSet devront être adaptées.

Nous utilisons un outil qui utilise RTTI pour présenter la liste des propriétés d'un composant BDE ne figurant pas dans un composant cible.

Voici la liste pour "tTable - tIbDataSet":

45_ttable_minus_tibdataset_properties_3



5.5 - Propriétés utilisés par NOS applications

Pour être francs, il y a là des propriétés que nous n'avions jamais utilisées ou dont dont nous ne soupçonnions même pas l'existence (KeySize, DisableConstraints).

Par rapport à cette liste générale, il est donc utile de trouver quelles propriétés sont effectivement utilisées par nos applications.

Nous avons un autre utilitaire qui analyse les sources de l'application et dresse la liste des propriétés à adapter effectivement utilisées.

Voici un exemple, limité à deux types, sur les échantillons d'unités à migrer:

41_twwquery_used_properties



Nous avons même un utilitaire plus précis qui nous montre le contexte d'utilisation des propriétés à adapter pour une liste d'Unités:

  • par composant BDE (ou descendant) utilisé, pour chaque propriété ne figurant pas dans le composant cible, les instructions utilisant cette propriété

    42_twwquery_properties_and_values

  • un KWIC (KeyWord In Context) d'une ou plusieurs propriétés qui ne nous sont pas familières, comme les PictureMask utilisés par InfoPower :

    43_twwquery_picturemask_kwic

    ou encore pour les propriétés que nous devrons modifier, comme par exemple Params si nous visons une migration vers ADO

    44_params_kwic

  • il est aussi possible de visualiser la recherche de plusieurs identificateurs dans un fichier .RTF. Ici nous avons recherché "RequestLive, SQL, ValidateWithMask" (mais pas les autres mots qu'il faudra remplacer comme DataBaseName ou UpdateMode). Voici une vue partielle du .DFM:

    10_find_in_file



5.6 - Propriétés les plus fréquentes

Les points que nous avons rencontrés le plus fréquemment sont
  • connexion
    • tDatabase, tSession
    • AliasName, DatabaseName
  • tTable
    • TableName
    • GotoKey, FindKey
    • SetRange
    • indexes
      • IndexDefs
      • index conditionnels dbx
    • UpdateMode
    • FindField
    • CreateTable
  • tQuery
    • UpdateMode,
    • RequestLive
    • Params (si passe en ADO)
  • tStoredProc
    • paramètres IN, OUT ou INOUT
  • tUpdateSql

  • traitement des blobs

  • transaction
    • traitement des transactions BDE explicites, fermeture des tDataSets
    • isolation
    • passage de transaction optimistes à des transactions pessimistes, g


5.7 - Stratégie de remplacement

Une fois que nous avons déterminé la liste des composants et propriétés BDE à adapter, il faut déterminer quelle technique de remplacement utiliser
  • remplacement textuel in situ (dans le code .pas / .dfm )
  • remplacement par appel de procédure / fonction
  • replacement par composant descendant


5.7.1 - Remplacement inline

L'idéal serait de transformer le programme existant comme si le code avait directement été écrit pour le composant cible.

Ainsi, par exemple

Table1.TableName:= 'invoice'
Table1.Open;

pourrait être remplacé par :

IbDataSet1.SelectSql:= 'SELECT * FROM invoice';
IbDataSet1.Open;



Cette technique ne fonctionne pas toujours aussi simplement.

  • ainsi, si TableName avait été affecté pour modifier la table (Edit / écriture, Post), ce n'est pas SELECT qu'il aurait fallu ajouter à IbDataSet1 mais UPDATE. Si de plus les instructions de modifications se font dans une procédure appelée après l'initialisation de TableName, cela devient tout de suite plus compliqué.

    Table1.TableName:= 'invoice';
    process_invoices(Table1); // <== SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ?

  • souvent aussi l'adaptation néccessite plusieurs lignes. C'est le cas pour des méthodes telles que FindKey ou les expressions d'indexe dBase. Il peut y avoir beaucoup de lignes dupliquées, avec les risques d'incohérence en cas de modifications ultérieures de l'une ou l'autre des version.


5.7.2 - Remplacement par appel de procédure

Lorsque le remplacement nécessite quelques lignes de traitement, le réflexe est de factoriser les lignes en question dans des Procedures ou Functions.

Donc :

Table1.CreateTable;

deviendrait :

create_table(IbDataSet1);



5.7.3 - Remplacement par Composant descendant

Ces procédures qui remplacent certaines méthodes ou propriétés BDE sont en général regroupées dans une Unité, et progressivement nous glissons vers un composant descendant du composant cible ayant des propriétés, méthodes et événements qui adaptent les traitements BDE aux composants cibles.

Nous sommes en fait en trait d'emprunter la voie des tIbQuery ou tAdoTable.

Il n'y a aucune honte à le faire. Toutefois nous nous éloignons d'une migration "transparente", qui fournirait un programme comme s'il avait été écrit directement pour les composants cible. Le code gardera un parfum de BDE.



Nous avons utilisé cette technique, par exemple,

  • pour doter IbDataSet d'un TableName, ce qui permettait de générer la bonne instruction Sql (SELECT, INSERT etc) en fonction du traitement recontré
  • pour éviter le chargement massif dans un tAdoDataSet à l'ouverture
  • pour remplacer des composants descendant de composants BDE (le traitement des tWwTable.PictureMask, ou des traitements de recherche de descendants de tQuery)


5.7.4 - Table de remplacement

Le résultat de ce bel effort est une liste des techniques à utiliser pour traiter chaque remplacement. Très souvent un mélange des trois techniques est le plus simple.

Dans tous les cas, il faut tester le bon fonctionnement de la technique choisie sur quelques exemples représentatifs. En général nous créons quelques projets séparés du projet à migrer avec la version BDE et la version adaptée.



5.8 - Les essais préliminaires de charge

Une fois le choix des composants cible et les techniques de transformation arrêtés, il est NECESSAIRE de procéder à des tests de charge sur des exemples réalistes correspondant à l'application.

Le cas caricatural, mais réel, est l'utilisation d'un SELECT * sur une table qui ne rend la main que lorsque toutes les lignes sont chargées (Ado en mode curseur client, DataSnap avec des tClientDataSet etc).

Les essais sur ces cas typiques permettront

  • soit d'optimiser les traitement (ajout de Where, utilisation de requêtes paramétrées, de procédures stockées, mise en place d'index etc)
  • soit de modifier les choix de composants et de transformations jusqu'à ce que ces points soient résolus.
Il ne s'agit pas de réglage fin de l'application mais de vérifier que la performance ne se dégrade pas de façon insupportable.

Et ces tests sont INDISPENSABLES, car le BDE tamponnait admirablement les données récupérées par l'application. Certes en 1995 nous ne savions pas que "SELECTFROM EDF" n'était pas raisonnable (que ferait l'utilisateur devant une dbGrid de plusieurs millions de lignes d'abonnées EDF), mais le BDE ne nous renvoyait pas en pleine figure l'absurdité de telles requêtes. Ado, Ibx, dbExpress, eux, ne se gêneront pas pour souligner les problèmes.



Et naturellement il vaut mieux vérifier que vos choix sont corrects avant de vous lancer dans les remplacements massifs, surtout si vous n'utilisez pas de mécanique permettant de refaire les remplacements automatiquement. Nous avons rencontré des cas où un client avait passé plus de 4 mois pour une migration manuelle, avant de pouvoir faire ses premiers essais, et se rendre compte de problèmes de performance avec les remplacement qu'il avait effectués.



5.9 - Migration .PAS et .DFM

5.9.1 - Technique de remplacement

A présent vient le temps d'effectuer les remplacements. Plusieurs options
  • remplacement manuel par l'IDE
  • outil automatique


5.9.2 - Remplacement manuel par l'IDE

Une première approche simple est d'utiliser l'IDE Delphi pour effectuer le remplacement.

Nous pouvons par exemple

  • supprimer Table1
  • poser un tAdoDataSet, puis
    • lui donner le même nom que Table1
    • modifier ses propriétés dans l'Inspecteur d'Objet


A par les cas très simple, cette technique est trop rudimentaire
  • une application importante peut avoir des centaines de composants BDE
  • si nous supprimons un tDataSet nous supprimons les champs persistents qu'il contient éventuellement
  • la réinitialisation des propriétés et événements est très fastidieuse et souvent insuffisante (par quoi remplacer RequestLive etc)
  • il restera de toutes les façons à adapter le code (Table1.GotoKey etc)


5.9.3 - Outil Find / Replace simple

Les outils de remplacement textuel simples, ou avec des pattern matchings plus sophistiqués peuvent éviter certains de ces défauts.

Si nous remplaçons tTable par tIbDataSet dans le .PAS et le .DFM, les champs persistants seront en effet préservés.



Notez que

  • il faut utiliser un outil de remplacement autre que l'IDE (GREP), car
    • nous ne pouvons pas, avec le "Find / Replace" de l'environnement Delphi modifier en même temps le .PAS et le .DFM
    • l'IDE impose un traitement fichier par fichier

  • un remplacement simple ne résoud que les adaptations les plus simples. Cette technique n'est pas tellement utilisable pour remplacer une affectation de propriété par un appel de méthode avec divers paramètres.
  • pour les informations non remplacées dans le .DFM, si nous affichons la forme, l'IDE nous proposera de supprimer les propriétés qui ne font pas partie du composant cible. Table1.IndexFieldNames sera ainsi supprimé de IbDataSet1. Il faudrait donc noter que la requête SQL devra utiliser un ORDER BY, par exemple
  • il reste des risques d'homonymes. Si une variable locale ou un paramètre s'appellent DataBaseName, il ne faut PAS les remplacer par Connection ou DataBase ou ProviderName
  • les outils de remplacement n'ont pas tous de journal ou de statistiques des modifications. Seul un DIFF permettrait de savoir ce qui a été remplacé


5.9.4 - Outil Find / Replace syntaxique

Les remplaceurs syntaxiques sont des outils qui se basent sur le contexte syntaxique pour effectuer le remplacement. Ils analysent les textes sources (.PAS / .DFM) en tenant compte de la grammaire de ces fichiers.

Les analyseurs syntaxiques sous jacents permettront de distinguer

  • DataBaseName en tant que propriété d'un tDataSet

    Var InvoicetTable;
      Invoice.DatabaseName:= 

  • DataBaseName utilisé comme nom de variable

    Var DataBaseNameString;
      DatabaseName:= 

Pour cela, comme le montre l'exemple précédent, il faut analyser les Var pour trouver que Invoice est un tDataSet, puis, en utilisant les listes de propriétés construites ci-dessus, extraire la propriété DataBaseName si elle est utilisée pour accéder à cette propriété de Invoice

C'est ce type d'outil que nous avons utilisé pour nos migrations.



5.10 - Analyseurs / transformateurs syntaxiques

5.10.1 - Recompilation des originaux

Nous préférons effectuer une recompilation des projets initiaux (.DPR, .DPK) pour nous assurer qu'il ne manque pas de fichiers.

La compilation garantit que nous pouvons effectuer les analyses correctes de dépendances des unités et des éléments BDE contenus dans toutes les unités importées par les clauses Uses.

Ceci n'est pas toujours possible, certains clients ne souhaitant pas nous transmettre l'intégralité de leurs sources (composants tiers qu'ils ont acheté avec des contrats NDA).

5.10.2 - Quels fichiers analyser

La façon la plus simple de trouver les fichiers à transformer est d'effectuer une recherche (récursive) des .PAS, .DFM,, .DPR, .DPK des répertoires où sont placés tous les fichiers à migrer.

Il y a un petit risque d'inclure des fichiers obsolètes non utilisés actuellement. Si nous utilisons un outil de transformation automatique, le coût de modifier quelques fichiers de plus n'est vraissemblablement pas très grand, sauf si ces fichiers, les plus anciens vraissemblablement, nécessiteraient des efforts de transformations supplémentaires (composants qui ne sont plus utilisés).



C'est pourquoi nous préférons, lorsque c'est possible, établir la liste des fichiers

  • par un examen des fichiers de regroupement (.BPG, .DPR, .DPK) et+ leurs fichiers annexes qui contiennent les SearchPathes (.DOF, .BDSPROJ, .DPROJ), puis une analyse récursive des Uses et éventuellement des fichiers inclus ($i ou $Include)
  • par une analyse de scripts de construction (MAKE, WANT, MSBUILD, FinalBuilder etc)
  • si la compilation des originaux est possible, la liste des .DCU permet de vérifier quels unités sont effectivement utilisées


5.10.3 - Préparation des fichiers

Les fichiers à traiter sont pré processés:
  • les fichiers .DFM sont convertis en .TXT si ce n'est pas déjà le cas (nous avons publié il y longtemps sur ce site un utilitaire pour le faire)
  • les fichiers .PAS .DPR et .DPK sont normalisés : pas de Tabulation, espaces en fin de ligne, accents dans les identificateurs (possibles en Delphi depuis Delphi 2009 avec Unicode).
    Naturellement ces contraintes sont liées à nos outils d'analyse. Nous pourrions très bien modifier nos analyseurs lexicaux pour accepter ces fantaisies, mais actuellement nous trouvons plus simple de normaliser les fichiers ASCII


5.10.4 - Recherche des noms à remplacer

L'objectif est trouver les noms à remplacer dans les .PAS et les .DFM. Par exemple, Customer.DatabaseName sera remplacé par IbCustomer.Database ou AdoCustomer.Connection.



Les identificateurs à remplacer sont

  • les noms de type (tTable -> tIbDataSet)
  • les noms des propriétés (Invoice.DatabaseName -> Invoice.Database)
Notez que
  • nous n'avons PAS besoin de remplacer les noms des composants BDE (Invoice ou Facture peuvent garder leur nom)
  • éventuellement TableFacture pourrait être remplacé par IbDataSetFacture, mais ce n'est pas une priorité. Nos outils ne le font pas actuellement mais ce serait trivial de faire ce type de remplacement


Il faut dans un premier temps repérer les noms de toutes les variables, paramètres ou membres de Classes pouvant contenir des propriétés, méthodes, événements propres au BDE et qui n'existent pas dans le nouveau jeu de composants.

Pour cela, il faut commencer par trouver tous les types, tTable, tQuery etc, et établir une liste des objets de ce type:

  • variables (globales, locales)
  • paramètres (méthodes, types procéduraux, méthodes anonymes)
  • attributs de Classes (tForm, tFrame, tDatamodule, Classes sur mesure)
  • instructions With t_xxx.Create Do


Il est fréquent que les objets soient définis dans une Unité et utilisé dans une autre. Il faut donc dresser ces listes par Unité en séparant la partie Interface (qui pourrait être utilisée par une autre Unit et la partie Implementation (qui ne peut être utilisé que par cette unité)



Nos outils effectuent une première passe qui créé pour chaque unité la liste de ses composants BDE. Voici la liste pour DATAMOD.PAS de MASTAPP:

47_bde_component_by_unit_list



Ces listes permettent aussi de séparer les Unités en deux séries:

  • celles qui ne contiennent aucun type ou variable BDE, et n'ont donc pas besoin d'être analysées
  • celles qui contiennent des informations BDE à traiter.
Mentionnons que pour cette analyse, il faut procéder en deux temps
  • établir la liste des éléments BDE pour les Interfaces pour toutes les Unités
  • pour chaque unité, tenir compte des unités importées par les clauses Uses pour vérifier si un identificateur est un élément BDE ou non


5.10.5 - Paramétrage des remplacements

Nous utilisons aussi des fichiers qui spécifient comment remplacer les principaux identificateur.

Voici un exemple simple pour une migration BDE -> Interbase (liste partielle)

48_bde_replacement_strategy



Les modifications qui ne sont pas précisées dans ces fichiers sont détectées et traitées dans le code.



5.10.6 - Remplacement dans le .DFM

Pour pouvoir effectuer les différents traitements (recherche, statistiques, remplacement, affichages, logs), il est plus simple de procéder en deux temps:
  • construire une arborescence objet en mémoire qui représente le .DFM
  • effectuer les différents traitements sur cette structure


Nous avons publié l'ancêtre de notre analyseur de .DFM, et notre outil de migration utilise une version plus avancée de cet analyseur (meilleure architecture, fonctionnalités plus nombreuses).



Parmi le traitements particuliers, mentionnons la suppression de propriétés, les commentaires, la traçabilité. Si IbDataSet ne possède pas de RequestLive, que devons-nous faire ?

Les .DFM ne possèdent en effet pas de commentaires. Nous ne pouvons ni annoter nos modifications, ni mettre en commentaire nos modifications.

La solution que nous avons adoptée est de reporter dans FormCreate, dans un commentaire, les commentaires que nous aurions mis dans un .DFM si cela avait été possible.

Ainsi, par exemple, voici (une partie) d'un .DFM avant et après la migration :

20_dfm_before_after

et les parties du .PAS avec les report des propriétés qui ont été retirées du .DFM ainsi que l'événement OnBeforeGaOpen ajouté :

22_pas_dfm_transfer

le tout avec un code couleur raisonnable (bleu les noms de variables BDE, violet les types BDE, jaune les valeurs littérales, rouge les modifications etc). Ces images proviennent de fichiers .RTF générés par le migrateur.



Notez que :

  • dans l'exemple précédent, nous avions utilisé
    • un descendant de tAdoQuery, qui avait une propriété ValidateWithMask, PictureMask etc
    • un descendant de tAdoConnection, placé dans un tDataModule dm_sales
    • les événements tForm.OnCreate et BeforeGaOpen ont été ajoutés

  • si FormCreate n'existe pas, nous ajoutons l'événement dans le .DFM et le .PAS.

    Ceci n'est pas toujours possible, comme pour tFrame ou l'héritage de tForm, qui nécessite une analyse récursive des .DFM parents pour savoir si l'ancêtre possède un OnCreate. Cette analyse est un pré-traitement sur toutes les unités, et le résultat est stocké dans un fichier .TXT qui est rechargé pour chaque migration.



5.10.7 - Remplacement dans les .PAS

Les remplacements dans les fichiers .PAS utilisent les fichiers annexes construits préalablement
  • la liste des types BDE et de leurs propriétés qui devront être modifiées
  • la liste des noms de variables, membres, paramètres BDE de l'Interface par unité
  • les fichiers déterminant comment remplacer les types et propriétés
  • la liste des formes héritées


Pour modifier un identificateur, il faut s'assurer qu'il est soit un type soit une variable / member / paramètre.

Il faut donc faire une analyse syntaxique.

Nous aurions pu utiliser un analyseur Delphi complet. Nous avons publié la grammaire de Delphi 5 complète, et disposons d'un tel analyseur pour Delphi 6.

Toutefois dans notre cas, nous n'avons pas besoin d'un analyseur complet. Il suffit de pouvoir détecter les éléments cités ci-dessus: types, variable, membre, paramètre.

Un analyseur lexical partiel suffit donc. Pour être plus précis, savoir que nous sommes dans une zone de déclaration ou d'instruction est important, mais lorsque nous sommes dans les instructions, savoir si nous sommes dans une condition d'un If ou entre Repeat et Until est sans importance.

Notre analyseur tient donc compte de

  • Interface et Implementation
  • Type, Const, Var
  • les Classes et Records
  • les en-têtes de méthodes
  • With
L'analyse se fait en deux temps:
  • une tokenisation du texte (liste linéaire pré-analysée des symboles) qui facilite l'examen de l'environnement proche d'un élément (par exemple pour les Params)
  • une descente récursive pour le texte en tenant compte des mots clé
Le résultat est une structure des remplacements à apporter contentant les identificateurs à traiter avec les informations sur leur type, position etc.



La liste des remplacements est utilisée pour

  • effectuer le remplacement en tenant compte des composants cibles et de la stratégie de remplacement adoptés pour chaque type / propriété cible
  • ajouter optionnellement des commentaire marqueurs dans le texte modifié
  • mettre en commentaire certaines instructions difficilement remplaçables (traitement de tSession, par exemple). Ces commentaires peuvent contenir des marqueurs permettant de les trouver facilement (par exemple "xxx_tSession") pour coder manuellement ces cas
  • générer les fichiers .RTF avant / après, les logs, les statistiques etc


Ces listes de remplacement sont ensuite fournies à des Classes qui mettent en oeuvre les remplacement:
  • une Classe de base, effectue les traitements simples (remplacement d'un type par un autre, par exemple)
  • ont des descendants pour chaque jeu de composants cible (ADO, Ibx, Dbx)
  • éventuellement des descendants spécialisé par migration client
C'est dans ces Classes que se font les transformations les plus complexes, nécessitant l'analyse du contexte d'un élément BDE pour le remplacer par une ou plusieurs expressions / instructions.



Voici un exemple simple de modifications de paramètres ADO:

  • le code avant les modifications

    30_bde_items_before

  • et voici le code modifié

    30_bde_items_after



5.10.8 - Inconvénients d'un analyseur syntaxique partiel

L'utilisation d'un analyseur syntaxique partiel n'est pas parfaite, car il peut provoquer des remplacement d'homonymes.

En particulier pour les noms tels que Value, Text ou Param, et surtout si le code contient des With.

La compilation détectera ces erreurs assez facilement.



5.10.9 - traitement des uses

Les clauses Uses sont l'objet d'un soin tout particulier.

Les Unités d'une clause Uses sont utilisées pour ajouter la liste

  • des Types de composant descendant BDE pour l'Interface et l'Implémentation
  • les noms d'identificateurs de variables, membres, paramètres pour l'Implémentation
Nous utilisons ici le même mécanisme que le compilateur, qui ajoute les identificateurs d'une unité citée dans une clause Uses à la liste des identificateurs propres à l'Unité analysée.



D'autre part il faut naturellement réaménager les clauses Uses

  • en supprimant toutes les Unités BDE : DBTABLES, BDE, DBIINT
  • en ajoutant, dans l'Interface ou l'Implémentation les unités des jeux de composants cible, plus les unités que nous aurions éventuellement ajoutées (comme les tDataModule pour les connexions, les composants que nous avons créés ou les librairies de méthodes remplaçant certaines propriétés)


5.10.10 - Regroupement des fichiers

Pour traiter les Unités, devons nous utiliser l'organisation des répertoires du client, ou copier toutes les Unités dans un répertoire de traitement, puis les recopier dans les répertoires clients ?



Il est plus facile de tout copier dans un répertoire unique, proche des répertoires contenant les fichiers de paramètres, de logs, journaux, statistiques, analyses et rapport de migration divers.

Cela nécessite de créer quelques utilitaires, notamment pour recopier les unités dans les bons répertoire d'origine.



La copie simple n'est pas toujours possible, en particulier si le client

  • utilisait plusieurs lecteurs pour répartir ses fichiers
  • plaçait ses fichiers dans des répertoires qui ne nous conviennent pas (\Program Files)
  • avait des Unités de même nom dans des répertoires différents
Nous avons rencontré une seule fois un cas d'homonymes massifs. Il s'agissait d'une famille de projets où certaines unités avaient été copiées avec le même nom et modifiées pour tenir compte du produit de la famille. Le traitement de chaque projet séparément était trop lourd (il y avait plus de 200 .DPR).

Une première solution est de renommer les unités (préfixes ou suffixes) mais cette solution que nous avons adoptée une fois est très lourde. Il faut en effet refaire une analyse syntaxique partielle, trouver des nom garantis uniques, et remplacer les noms des unités (en-tête, Uses) sans oublier les noms utilisés pour qualifier les identificateurs. Les Unités ayant ainsi toutes un nom différent peuvent alors copiées pour la migration. Mais il faudra ensuite supprimer les suffixes / préfixes et recopier dans les bons répertoires les unités traitées.

L'autre solution est naturellement de travailler sur les répertoires d'origine, ce qui rend l'accès aux sources plus difficile mais évite des changements de noms d'unités.



5.10.11 - Compilation des .DPR et .DPK

Le but de la transformation automatique est d'arriver à compiler tous les .DPR et .DPK

A partir de là nous pourrons examiner le fonctionnement de chaque unité.

C'est parce qu'il est souvent long et compliqué d'arriver à compiler les projets / DLL / composants que beaucoup hésitent à migrer le BDE. Les transformations partielles sur quelques unités d'un gros projet ne peuvent pas être effectuées sans modifier pratiquement toutes les autres unités. Et si les modifications sont effectuées manuellement, les tests pour vérifier que les choix de remplacement sont corrects et efficaces ne pourront être effectués que lorsque tout le travail aura été effectué, peut être des mois plus tard.

Nous avons déjà préconisé

  • les essais des remplacements choisis sur des projets simples contenant une Unité et quelques composants BDE
  • la vérification de la charge sur des données réalistes
Mais c'est la possibilité de compiler le projet final qui est le point de passage obligé pour complètement tester et finaliser la migration.



5.10.12 - Resteront à modifier

Un outil automatique ne pourra pas tout modifier.

Citons, par exemple

  • le traitement de certains composants BDE tels que tSession
  • les modifications qui portent sur plusieurs instructions Delphi. En particulier les instructions qui traitent les tables comme des fichiers (des boucles FindFirst, avec effacement de fichiers, copie de fichiers entre répertoires etc)
  • les transformations sémantiques, telles que le mode de verrouillage pessimiste ou optimiste, ou la gestion des transactions en général
  • les modifications qui nécessiteraient des modifications du code Sql (procédures cataloguées que le client peut préférer gérer lui-même)


Notre outil nous permet de mettre en commentaire (avec marqueur en général) les instructions que nous ne traitons pas et qui bloqueraient la première compilation.

Mais il faudra donc intervenir manuellement pour finaliser la migration.



Naturellement, si le client nous donne mission de fournir un produit fonctionnel, nous pouvons aussi effectuer ces modification. Mais elle nécessite une connaissance métier du domaine d'application, une compréhension du fonctionnement du produit, un apprentissage de l'utilisation du produit, la fourniture ou l'accès aux données réelles pour pouvoir effectuer les tests d'acceptation etc. Si le client a développé le produit, il est en général plus économique qu'il se charge de cette partie.



5.11 - POC - Migrations Partielles

Un client pour lequel nous avions organisé une formation Objets Métier a effectué sa migration Delphi "une table à la fois". Il a ainsi créés des objets métier un à un (facture, client, stock etc), et en a profité pour gérer la parties Sql avec des composants non BDE. Néanmoins le traitement de plusieurs tables appartenant à des moteurs Sql différents (jointures, relations maître détail) ne doit pas être trivial.

Donc pour notre part, nous avons toujours fait une migration de tous les composants BDE d'un projet (et par conséquent d'un groupe de .DPR, .DPK)



En revanche il nous est arrivé de faire des migration pour démontrer au client la faisabilité (et évaluer concrètement le coût final) d'une migration.

Le client sélectionne alors les unités représentatives d'un projet qui servira de POC (Proof Of Concept). Dans notre cas, il s'agissait de 300 unités (sur les 4000 unités) avec les .DPK des composants et un .DPR.

Dans le cas d'un projet de gestion style ERP, il est possible, par exemple, d'isoler les ventes ou la gestion de stock, avant de migrer toutes les applications (utilisant en général une mécanique de plugins COM ou .BPL).

Après que le POC a été migré avec succès, nous avons pu réutiliser un grande partie du travail (surtout les composants et la plupart des unités) pour la migration de l'ensemble complet.



5.12 - Reporting de la migration

5.12.1 - Statistiques

Nos analyseurs génèrent toutes sortes de statistiques, dont nous avons présenté quelques exemplaires partiels ci-dessus
  • composants BDE par unité
  • liste des types / propriétés BDE pour toutes les unités
  • les des type / propriétés / valeurs BDE, éventuellement sous forme de KWIC, global ou par unité


Nous établissons aussi en commun les composants cibles à utiliser, et optionnellement les stratégies de remplacement de chaque propriété / méthode / événement



Nous générons aussi des statistiques de remplacement, mais elles sont bien moins utiles que les logs.



5.12.2 - Logs

Nous utilisons, comme pour toutes nos applications, de logs des transformations effectuées.

Nos logs détaillés sont surtout un outil pour affiner nos transformations, surtout utilisés lors de la mise au point des transformations sur des projets simples.

Mais nous générons en plus des logs

  • soit pour donner au client un historique des modifications
  • soit pour signaler les modifications qui restent à effectuer
49_migration_log



Il nous est aussi arrivé de générer des logs sur mesure demandé par le client pour récapituler, par unité, la présence de certains éléments BDE, par exemple



5.12.3 - Fichiers .RTF avant / après

Pour avoir une vue instantannée des modifications à effectuer ou effectués, nous générons des fichiers .RTF avec les éléments BDE ou les éléments BDE en couleur.



5.12.4 - Balises dans le code

Les modifications délicates (transactions) ou incomplètes peuvent être marquées dans les .PAS, .DPR, .DPK par des balises en commentaire qu'il est facile de retrouver par une recherche depuis l'IDE

Naturellement les modifications triviales (remplacer tTable par tIbDataset) ne sont pas accompagnées de marqueurs.



5.12.5 - Weekly Report

Pour les migrations de plusieurs semaines, nous avons utilisé des rapports hebdomadaires des progrès réalisés.

Nous avons utilisé le format des "Merit Review" de Texas Instruments

  • les résultats (composants migrés, essais limités de transformations réussis, compilations réussies, modifications manuelles réalisées etc)
  • les problèmes principaux rencontrés (unités manquantes, transformations difficiles)
  • les actions prévues et le planning prévisionnel
Ce résumé en une page (avec des annexes plus détaillées au besoin) était la base d'échanges téléphoniques ou sur place et permettaient de suivre plus facilement le déroulement de la migration.



5.13 - Les Tests

5.13.1 - Le Graal Delphi

L'idéal serait que l'application soit déjà équipée avec sa mécanique de tests (tests unitaires, tests d'intégration, test d'acceptation).

Pour le moment, nous n'avons jamais recontré ce cas.

Bien que Smalltalk (le berceau des objets) date de 1980 et Delphi de 1995, les mécaniques de test unitaire telles que dUnit ne sont apparues que vers 1999. Les premiers projets utilisant le BDE datant de 1995 n'utilisaient pas de test unitaires.

A ce jour, les tests ne font pas partie de la culture générale Delphi.

Mentionnons toutefois qu'une prise de conscience du problème est en train de s'opérer, et affecte pour le moment surtout les équipes importantes sur les projets nouveaux.

Nos remarques pourront apparaître comme un peu moralisatrices, mais nous sommes actuellement dans le même panier pour la plupart de nos projets anciens (notre traitement de texte qui nous permet de construire cet article, notre facturation / comptabilité, bref nos premiers projets Delphi que nous utilisons néanmoins tous les jours !).

En attendant, les projets à migrer que nous avons été amenés à examiner (audits) ou à traiter ne sont pas dotés de tests.



5.13.2 - Mise en place de tests

Plus grave, ces projets anciens sont en général affligés d'un mélange tForm / composants d'accès aux bases de données / traitement métier. Les tests se ramènent donc à des scripts de simulations de l'interface utilisateur.

Mettre en place des test qui vérifieraient que nos modifications n'ont pas modifié le comportement des projets reviendrait donc à

  • mettre en place les tests sur le projet original
  • migrer et vérifier que les test sont encore passés avec succès
Nous n'avons pas à ce jour effectué ce type de vérifications.



5.13.3 - Test Unitaire des modifications

Nous utilisons des test unitaires sur les modifications élémentaires des types et des propriétés et méthodes.

Mais comme la migration n'est pas toujours complète, il faut compléter par des tests d'integration et d'acceptation



5.13.4 - Tests via l'Interface Utilisateur

Comme expliqué précédemment, nous n'effectuons en général pas ces tests tout seuls:
  • nous ne sommes pas des experts de l'utilisation des applications
  • il est fréquent que nous n'ayons pas un accès complet aux données, que ce soit les données réelles, des données de développement ou des Mocks


Le mieux que nous puissions faire est
  • soit de faire les tests avec le client
  • soit de laisser le client effectuer les test, et, en cas de problème, nous faire expliquer comment reproduire l'erreur et mettre en place un script qui nous permette de simuler les actions utilisateur localiser le problème, l'intégrer à nos tests unitaires et effectuer les remplacements ou indiquer comment les effectuer


Dans le cadre d'une mise au point collaborative (le client essaie les actions qu'il souhaite vérifier, et nous corrigeons les problèmes éventuels), quelques recommandations :
  • intégrer un log des instructions Sql appelées
  • ajouter un enregistrement des actions utilitaires pour pouvoir automatiser les scripts de test



6 - Migrations multiples

6.1 - Migration de version Delphi

Il est fréquent que les migrations BDE s'accompagnent de migration de version Delphi, par exemple de Delphi 5 à Delphi Xe3.

Le problème essentiel est la migration Unicode. Cette migration n'est pas le sujet de cet article, qui a été évoquée dans notre article Migration Delphi Unicode

Nous mentionnons ce problème uniquement car lorsque l'objectif est une double migration, la question se pose de tout migrer en une fois, ou passer par une migration BDE, ou une migration Unicode d'abord.



Une migration 64 bit rentre dans le même cadre.



Pour notre part, nous recommandons une migration en deux étapes

  • une migration BDE d'abord
  • une migration vers la nouvelle version Delphi ensuite
Parmi les raisons:
  • le bon fonctionnement de l'accès aux bases de données est bien plus important que la partie version Delphi / Unicode. Il vaut mieux garantir le bon fonctionnement de cette migration en premier
  • le client sera plus à l'aise pour effectuer ses vérifications avec son environnement habituel


6.2 - Migration de librairies

Lors des migrations (BDE et / ou Unicode), les librairies tierces sont souvent la source de problèmes lors de migration:
  • ces librairies sont souvent proches des couches matériel (gestion de périphériques spécifiques)
  • elles sont en général optimisées, utilisant par exemple force FillChar, Move et autres pChar ou appels Dbi (appels directs à la .DLL BDE) qui sont à verifier de près à cause d'Unicode
  • les sources n'ont pas été (ou il n'était pas possible) achetées


Si les sources ne sont pas disponibles, il n'y a pas d'autre choix que de remplacer les composants correspondants.



Pour les librairies dont le client a acheté les sources, la meilleure recommandation est de vérifier sur Internet s'il existe une version migrée, soit par la société qui commercialise les produits (InfoPower, Tms etc), soit par des bénévoles (Turbo Power).

Il est d'ailleurs regrettable qu'il n'y ait pas eu un effort commun de centralisation d'une liste des composants traduits.



6.3 - Compilation Automatique - DCC

Lorsque les projets à migrer comportent de nombreux .DPK et .DPR, il est impératif d'utiliser des scripts de compilation. Lancer de nombreuses compilations peut très vite devenir fastidieux.

Tous ces scripts utilisent à la base DCC32.EXE (le compilateur en ligne Delphi).

Plusieurs outils sont disponibles

  • MAKE
  • MsBuild
  • WANT
  • FinalBuilder (disponible depuis Delphi 2010)


Nous avons pour nos propres besoin construit un outil qui lance DCC32 par WinExec, mais qui a une interface graphique pour
  • analyser ce qui est installé
  • permet de sélectionner les répertoires sources ou utiliser une liste de .DPK / .DPR
  • présente un ordre de dépendances pour les .DPK.DPR inter dépendants (tri topologique)
  • utilise des fichiers de paramétrage des options et chemins à utiliser
  • installe les packages
  • récupère les logs d'erreurs de DCC32, en permettant de sélectionner une erreur et d'afficher le .PAS ayant causé le problème.


6.4 - Changement d'architecture

Il est tentant de profiter d'une migration BDE pour effectuer des transformations plus profondes:
  • refactoriser le code
  • utiliser d'autres jeux de composants (Ribbon Controls, composants TCP/IP, composants graphiques, composants XML ou Office, composants de reporting, notamment pour remplacer Crystal Report)
  • séparer la partie visuelles des composants d'accès aux données et des règles métier (tDataModule, objets métier, ORM complet, architecture multi-tiers, service web)


Il nous semble préférable d'effectuer ces traitements après une migration BDE, mais tous nos clients ont entrepris ces modifications de front.




7 - Evaluation des coûts

7.1 - checklist - coûts

Tous les éléments de cet article interviennent naturellement lorsque nous évaluons les points à surveiller pour une migration.

Mentionnons pour résumer

  • y a-t-il une migration des données
  • l'application utilise-t-elle déjà Sql ou uniquement des tTable
  • le code utilise-t-il les CachedUpdates
  • comment sont gérées les transactions et les utilisateurs multiples
  • y a-t-il déjà d'autres jeux de composants d'accès autres que le BDE
  • quelles librairies externes sont utilisées
  • quelle est la version Delphi de départ et la version cible


7.2 - Coûts

Présenter un mode de calcul du coût de migration relève de l'impossible.

Tentons néanmoins de fournir quelques éléments.



Prenons par exemple une application importante. Par exemple

  • 1 million de lignes
  • 2.000 à 3.000 unités
  • 100 à 500 tables
Tout d'abord toutes les unités ne sont pas concernées par la migration. Des formes comme les Login ne contiendront en général aucun élément BDE et ce sont les unités qui appellent le Login qui utiliseront peut être une table pour vérifier un utilisateur. Selon notre expérience, environ 30 % des unités sont concernées par la migration BDE, moins si le code est bien séparé entre partie visuelle et bases de données / traitements métier.



Nous pouvons commencer par une estimation de la migration d'une unité utilisant 5 ou 6 tDataSet, avec une dizaine de propriétés à modifier, certaines simples (changement de type) ou plus complexes (paramètres, CreateTable).

En modification manuelle, il est raisonnable de compter 1 heure par unité à modifier. Si nous avons à modifier 200 unités environ, cela fait 200 heures, soit 25 jours. Il faut ajouter à cela le temps de test.



Partant de ce type d'évaluation simplette, nous pouvons ensuite ajouter des coefficients d'ajustement:

  • - 20 % si seuls des tQuery sont utilisés avec un tDatabase commun
  • + 20 % si le code utilise beaucoup de tTable
  • + 10 % s'il faut effectuer une migration Unicode
  • + 20 % si la migration Unicode doit impliquer de nombreuses librairies tierces non disponibles en version Unicode
  • + 20 % pour les tests si le code mélange systématiquement les tForm, les composants d'accès et le code métier


Les quelques rares blogs assez précis sur le sujet, ainsi que notre expérience laissent entendre que pour ce type de migration, il faut prévoir une durée de 3 à 5 mois.

Avec une variation possible de quelques semaines à 7 mois !.




8 - Prestations de l'Institut Pascal

Nous offrons, vous l'avez compris, des services pour vous assister dans la migration BDE. La gamme est très vaste
  • présentation générale de la migration BDE, tenant compte d'une information générale sur le code client (mais sans examen détaillé des sources). En général une demi journée à une journée
  • simple audit : vous nous présentez (sur site ou par envoi Internet) tout ou partie du code à migrer. Nous utilisons nos outils d'analyse et vous présentons un rapport d'une dizaine de page comportant
    • notre analyse de la situation
    • nos recommandations pour la migration
    • une évaluation de la durée et des coûts

    Pour une analyse sur site, prévoir 1 à 3 jours
  • fournitures de la liste des points à migrer par unité (liste textuelle et fichiers .RTF)
  • migration des données Desktop - fourniture de code source du transfert
  • migration par nos soins vers les jeux de composants sélectionnés en commun. C'est la prestation la plus fréquente
  • migration clé en main. Le produit livré fonctionne dans sa version sans le BDE et avec une performance équivalente à celle avec le BDE. Cette mission nécessite néanmoins une assistance client pour nous familiariser avec le produit.
Soulignons que les techniques que nous avons présentées ci-dessus peuvent aussi s'appliquer à d'autres transformations textuelles Delphi.



Vous pouvez nous contacter

  • jcolibri@jcolibri.com
  • Tél 01.42.83.69.36 - 06.87.88.23.91



9 - Références

Quelques références
  • dUnit
      dUnit, créé au départ par Juanco ANEZ
      article Wikipedia

  • Migrating Borland Database Engine Applications to dbExpress
      Bill TODD - Sept 2002
      Borland White Paper

  • Migrating Data from TDataSet to SQL DBMS
      Bob SWART (+- 2006)


Et quelques articles sur ce site Ainsi que quelques formations :
  • Formation de Delphi 7 à Delphi Xe3
    • mise à niveau concernant les nouveautés apparues dans les version Delphi après Delphi 7 : l'environnement, la librairie (génériques, anonymes, RTTI), les composants (Ribbon Controls, Visual LiveBindings, Live Tiles Windows 8), l'ouverture vers Mac et les mobiles (FireMonkey), les outils intégrés (Version, Profilage)
  • Formation ADO
    • gestion de données en utilisant ADO - Connexion, création lecture et écriture de Tables, position du curseur, batch updates, gestion des transactions et multi-utilisateur, transfert Excel et HTML, migration BDE
  • Formation Interbase / FireBird
    • Création de la base, tIbDataBase et tIbTransactions, lecture écriture et modification de données, les transactions, procédures cataloguées et événements, extraction, composants d'administration
  • Formation Bases de Données Multi Tiers
    • architecture générale, connection et transactions, tDataSetProvider, le tClientDataSet et le mode nomade, réconciliation des données, méthodes serveur



10 - Vos Commentaires

Comme d'habitude:
  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



11 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: mar-13. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
      + programmation_oracle
      + interbase
      + sql_server
      + firebird
      + mysql
      + xml
      – paradox_via_ado
      – mastapp
      – delphi_business_objects
      – clientdataset_xml
      – data_extractor
      – rave_report_tutorial
      – visual_livebindings
      – migration_bde
    + web_internet_sockets
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Audit Delphi Bilan technique (technologie, méthodes, architecture, organisation, techologie et version, formation, gestion de projet) et recommandations - Tél 01.42.83.69.36
Formation de Delphi 7 à Xe3 Présentation des nouveautés de Delphi 5, 6, 7 à Delphi Xe, Xe2, Xe3: nouveautés Rtl (génériques, anonymes, RTTI), Vcl (LiveBindings, FireMonkey, Mobiles), outil - 5 jours
Migration Delphi migration de versions Delphi, migration Unicode, migration BDE / base de données, migration Internet - Tél 01.42.83.69.36
Formation UML et Design Patterns Delphi Analyse et Conception Delphi en utilisant UML et les Design Patterns - 3 jours