sans titre 1
Microsoft a réécrit un nombre
incroyable de fois sa couche abstraite de moteur d’accès aux données et les
outils correspondants de mappage (ODBC, DAO lui a succédé, ADO, ADODB, ADO.NET,
Linq, LinqToSql, Entities, WCF, WCF-Entities for Silverlight, Entities
code-first).
Tout cela n’est qu’insistance
pour que les développeurs redéveloppent dans chacune de ces écritures
successives, mais suite à de véritables échecs de conception.
De ce fait, et dû aux
imperfections toujours restantes, bien des entreprises ont carrément écrits et
définis autrement leur propre moteur, ainsi que l’outil de mappage
correspondant) et n’en bougent plus, car le cœur de Visual Studio.Net reste,
indéfiniment, de 2002 jusqu’à maintenant.
ODBC fut le premier moteur, très
sommaire mais très professionnel, qui permet surtout d’exécuter des requêtes et
de recevoir le résultat. Sa simplicité et son intégration à Windows fait qu’il
est toujours utilisé lorsqu’on développe un logiciel un peu décalé des chantiers
généraux.
Ensuite, vint DAO, qui fut
meilleure couche qu’ADO, pourtant sa suivante, mais ADO était la seule couche
adaptée à certaines nouveautés des bases de données, la seule couche à avoir un
événement de fin de chargement, pour déjà faire du multithread, ainsi que du
chargement incrémentiel ou déconnecté. Mais cette nouvelle couche ADO était
complexe et technique, alors que DAO était intuitif. Les évolutions d’ADO pour
s’adapter aux nouvelles bases de données ont donné ADODB, toujours complexe et
technique, ce n’était pas une grande évolution. Pas de regard aux pauvres
bidouilleurs qui appréciaient particulièrement DAO.
Des outils de l’interface
graphique s’interfacent directement avec eux, mais dans ces versions, ils
plantaient toujours beaucoup et ne permettaient aucune personnalisation, aucune
gestion du moindre cas particulier ou spécial qui pourtant survenaient en
permanence.
De nombreuses versions ont
existé de ces trois moteurs. Des évolutions du numéro de version. Pour les
utiliser, il faut lancer leur installateur, commun, qui installe toutes les
versions de tous les moteurs, sur le poste du développeur comme celui de
l’utilisateur. Les objets de certaines de leurs versions n’étaient liés à aucune
documentation ni exploration d’objets en automatique, ni correction automatique,
ni compilation très contrôlée.
Ces trois moteurs sont devenus
obsolètes car Visual Studio.Net a mis du temps à être développé, il a coupé
toutes les chaînes de sorties de version pendant longtemps, mais il a un tel
cœur bien pensé (en plus Microsoft l’a normalisé officiellement donc sans brevet
rémunérateur), que rapidement après sa sortie (vers 2005, quelques versions
quand-même après), il fut devenu logiciel unique de développement Microsoft des
nouvelles applications en vue, suivant les espérances de Microsoft. La première
grille de Visual Studio n’était pas du tout viable. En 2005 est sorti la
DataGridView, à priori suite à mes suggestions de façon non négligeable.
Sous Visual Studio.Net, ADO.Net
reprenait dès le départ le flambeau des versions antérieures, car il avait été
nécessaire de réécrire un moteur dans le nouveau langage auquel il appartenait,
bien profitablement.
Pour la première fois, des
outils sont apparus afin d’effectuer un mappage automatique de la base de
données, c’est-à-dire une génération automatique de classes représentant les
tables, avec leurs membres représentant les champs, et toutes les fonctions
environnementales qui vont bien pour les gérer, et des fonctions de
transformation et de surcharge supplémentaires à l’occasion.
Pour ADO.Net, les outils de
l’interface graphique connectables ne plantent pas, contrairement aux outils
avant l’an 2000, mais on ne peut pas se détourner du schéma classique si on les
utilise, ce qui est extrêmement contraignant. La personnalisation possible n’est
pas caduque du tout, mais linéaire, avec quand-même si on le désire des accès
programmatiques. Mais ne confondons pas outil graphique connecté aux vraies
données, et DataGridView personnalisable indépendant si on le désire de tout
aspect extérieur et données.
Jusqu’à ADO.Net, les moteurs
d’accès aux données géraient l’état modifié des données. Toujours de façon plate
et brute, sans personnalisation possible des principes généraux. L’évolution,
c’est qu’il y ait des principes généraux personnalisables dans ces procédés. Tel
que la foison de manières d’annuler, de modifier en cascade les tables enfants,
de recopier la clé créée à l’enregistrement, modifier l’ordre d’enregistrement,
faire une transaction…
L’idée de Microsoft a été que
pour tous les moteurs qui ont suivi, le développement du mode Modification en
cours et Annulation est à notre charge.
Premier point d’achoppement. Un
énorme énorme travail systématique à réaliser les moteurs qui ont suivi pour
mettre au point des procédés de gestion d’état.
Autre point d’achoppement, les
moteurs qui ont suivi ne gèrent pas la représentation des champs de manière
archétypée, par exemple avec un objet Column. Donc un travail à faire champ par
champ, y compris pour le procédé de gestion d’état. Microsoft aurait pu bien
tirer les conclusions sur les principes à gérer et à personnaliser, et fournir
un outil, pourquoi pas indépendant, de gestion des états.
Le stockage des données est
désormais effectué dans une variable par champ, chaque cellule a son membre,
plutôt que dans un tableau sans contrôle de type mais privé. Cela retire des
possibilités de forçage du type de données, par exemple pour faire une
transformation, dans un cas particulier par exemple, tel que stocker une erreur,
mais permet des traitements rapides.
Pour autant, le principe de
colonne existera toujours dans le principe de tableau, sans que pour autant on
ait forcément un numéro de colonne invariable pour un champ. On n’aura pas
facilement ici d’outil pour ne pas charger toutes les colonnes, pour
n’enregistrer que les valeurs modifiées, pour se souvenir si une valeur a été
modifiée, pour ne pas créer toutes les colonnes. C’est discutable, c’est
quand-même propre, bien que je n’aie pas suivi ce principe dans la grille que
j’ai développé, mais c’est sans grande importance vu les qualités et les
défauts. Il est important par contre d’avoir soit un proxy dans un sens, soit un
proxy dans l’autre, afin d’avoir une façade de représentation de la table au
format du code et fortement typé.
Cette révolution vers LinqToSql
a été nécessaire car la nouvelle idée de Microsoft est que dans notre code, nous
ayons accès à la base uniquement comme de vrais objets de programmation objet,
fortement typés. Le designer génère un mapping dont les DLLs de LinqToSql
populent les classes elles-mêmes grâce aux noms des membres et des classes, et
quelques-unes de leurs métadonnées. Le code généré automatiquement est donc tout
petit, cette fois-ci.
Hélas, troisième point
d’achoppement, certaines nécessités nous renvoient à la nécessité de passer par
des requêtes, par exemple quand on ne connaît pas la colonne qu’on interroge ou
qu’on veut interroger les nouvelles colonnes.
Une couche indépendante de
requétage directement dans le code, avec contrôle fort du typage, avec gestion
de la syntaxe et aide et information à la saisie, appelée Linq permet
d’interroger à la manière de requêtes, ou même utiliser les fonctions de
requêtes à la manière de syntaxe normale, pour interroger n’importe quelle
source, donc les objets, LinqToSql, et aussi les classes WCF.
WCF est un outil pour que les
programmes communiquent entre eux en simulant un partage de l’arborescence des
vrais objets à l’intérieur. Un générateur permet de générer du code qui partage
sur l’intranet ou internet une structure. Cela permet de faire en sorte que le
programme accède à la base par des outils en tout point contrôlés et uniques sur
le serveur, ces classes WCF, c’est là que se trouve l’accès à la base. Point
d’achoppement, on est obligé de générer cette arborescence dans le programme
destination, par un outil. On n’a pas d’outil simple pour accéder facilement à
un service WCF sans le connaître.
Un genre de mélange entre
LinqToSql et WCF a été aussi créé pour permettre à Silverlight de communiquer
avec une base entièrement à distance. Silverlight était un genre de plugin Flash
professionnel et sécurisé, fait par Microsoft, pour faire des travaux que ne
permet pas le protocole html, mais qui est tombé à l’eau face aux nombreux types
d’explorateurs qui n’auraient jamais pu le supporter, particulièrement les
mobiles.
LinqToSql a évolué dans la même
version de Visual Studio qui l’a vu naître, à partir de son premier Service
Pack, vers Entity Framework. Cela n’a pas remis en cause l’intérêt de LinqToSql,
qui par-ci par-là était plus simple et de même nature.
Toujours dans la même version,
un second Service Pack est venu délivrer une version de Entities qui permettait
de ne pas faire hériter les entités d’une classe propre à Microsoft, donc
permettait d’hériter de la sienne propre. Intéressant pour gérer des choses du
genre mode modification, fonctions communes et considération des champs d’une
manière générique.
Cette nouvelle formule permet
particulièrement la mise à jour du designer sans devoir recréer les objets, et
permet de forcer le nom des membres qui seront utilisés dans le programme. Elle
permet aussi d’indiquer des règles de contrôle et de translation, un peu plus de
souplesse, et un peu plus de maîtrise de la partie code. Elle permet de modifier
les objets dans le designer, et de propager les modifications dans la base et
dans le code.
Les métadonnées sur les colonnes
sont stockées dans LinqToSql dans les attributs des méthodes, et dans Entities
dans des fichiers ressources. Dans les deux cas, ce n’est plus le code généré
qui traite directement les cas particuliers. C’est une information traitée par
un moteur, donc limitée, lente en ce qui concerne les attributs de méthode.
Point d’achoppement, forcer le
nom utilisé dans le programme est très demandé, est intéressant, mais en même
temps tellement inutile que beaucoup s’y sont perdus entre les deux noms. Jamais
on ne change le nom en base de données, mais en même temps, rien ne sert de le
corriger ou de le fixer dans le code. L’erreur restera, et l’utilisateur ne le
verra pas.
Mais on se rendait compte qu’on
faisait évoluer les designers pour répondre aux besoins, mais qu’en réalité, le
besoin d’une classe est infini. L’intégralité des possibilités de paramétrage,
de croisement, de croissance doit lui être possible. Jamais on ne pourrait tout
déterminer avec un designer. Depuis Visual Studio.Net, on peut ajouter notre
propre code au code auto-généré, dans les points d’action prévus par Microsoft,
ou par héritage. LinqToSql ne le permet pas utilement.
Bien des entreprises se sont
rendues compte de cela, et ont créé leur propre générateur. Parfois en générant
la base complète, rendant inutile le passage par un designer, pour peu qu’on ait
de nombreux points d’accès dans les classes ainsi générées. Utilisant tout de
même le moteur DLL de LinqToSql ou Entities, ou redéveloppement le leur, ça
arrive, dans ce cas, il passe souvent par des requêtes. Mais il ne faut pas se
leurrer, derrière Linq, il y a toujours jusqu’à maintenant pratiquement que des
requêtes.
Suivant ce constat aussi,
Microsoft a écrit un nouvel Entities, Entities code-first, qui donne la priorité
au code concernant les métadonnées de description des classes. C’est dans le
code qu’on définit les paramètres du designer, lui ouvrant ainsi beaucoup plus
de possibilités. On peut même ne passer que par le code, écrire d’abord ces
classes qui correspondent à des tables directement par le code. Ensuite, à
l’occasion, le designer les affichera, en lisant le code, méthode des débuts de
Visual Studio.Net, et non en retenant sa propre structure. On croit souvent que
code-first veut dire non database-first, mais cela fait longtemps que les outils
sont tout autant designer-first. Code first veut dire non designer-first.
Cette
méthodologie apporte un autre avantage, c’est que lorsqu’il y a beaucoup de
tables, ou des tables dans des bases ou des univers dispersés, il fallait faire
plusieurs fichiers Entities par le designer, qui sont pourtant sensés se
partager certaines tables, qu’il faut donc définir plusieurs fois, afin que
l’interaction ne soit pas coupée. Mais dans ce cas, il faut dupliquer les
décorations de ces classes, les héritages, et le polymorphisme entre ces tables
sera coupé. Très important pour un développeur pourtant. La méthode code-first
permet de définir les tables sans forcément l’inclure dans un designer précis.
Ce qui permettra même des lectures et des modifications inter-univers.
Il en va à conclure par les
imperfections toujours existantes :
Jamais eu de procédé de gestion
des états de modification parfaitement fonctionnel et adapté à tous les cas,
qu’il faudrait lister (j’en possède une classe intéressante). Il est vrai que
les fonctions proposées par Microsoft étaient inscrites en dur dans les classes
ancêtres de base de données, et étaient fermées à toute utilisation large,
nécessitant d’adapter le code à leur fonctionnement. On était obligé de passer
par elles pour gérer les modifications :
Pas d’état nouveau modifié, pas
de possibilité d’avoir un état d’unicité ne provenant pas de la base, pas de
gestion des valeurs nulles propre, pas de gestion du readonly spécifique à la
ligne, pas de gestion du readonly spécifique à l’utilisateur et pas au code, pas
d’outil pour repasser sur les valeurs saisies ou les données lues, une vue en
lecture seule ne peut pas être forcée en mémoire (toujours le cas dans
Entities), pas de possibilité d’avoir accès à la nouvelle ligne de la grille
dans la classe mappant la table en tant que ligne de la table, pas d’annulation
en cascade, pas de possibilité de gérer si on écrit toute la ligne ou les champs
modifiés…
Pour autant, j’ai connu un des
grands déboires de logiciel en ce qui concerne la réécriture de ce procédé pour
le mode Entities par un collègue.
Régression au niveau des
métadonnées des colonnes : Dans les versions ADO… et DAO…, les colonnes étaient
représentées par un objet héritant de DataColumn, qui stockait toutes les
précisions nécessaires et plus concernant la gestion de la colonne elle-même,
sans stocker sa valeur. Il est vrai qu’il n’était pas héritable et ne gérait pas
les cas qu’on aurait voulu personnaliser. Mais elle permettait de traiter la
colonne de façon générale, sans la connaître, par exemple dans le procédé de
gestion des modifications, dans la zone de saisie (contrôle) de la colonne où on
aurait accès à des métadonnées directement, ou pour gérer les nouvelles colonnes
ou des colonnes dynamiquement.
Cela serait aussi un moyen
d’approche pour retrouver la fonctionnalité de pourvoir accéder aux colonnes
dans la base sans avoir le nom dans un mappage fortement typé.
Les métadonnées des colonnes
sont stockées dans les premières versions de Entities dans les attributs des
méthodes. Mais c’est d’une lourdeur ! Pas de possibilité de calcul, de
paramétrabilité avec des fonctions, pas de métadonnées sur les métadonnées,
métadonnées difficiles d’accès, lenteur d’accès à ces métadonnées…
Au contraire, c’est une idée à
suivre que le principe de stocker les métadonnées dans des classes de
métadonnées, portant le même nom que le membre associé, et transportées avec par
une classe qui les stocke et qui est aussi passée en paramètre, pourquoi pas la
même classe parent.
C’est le cas de Entities
Framework code-first.
Frédéric Decréquy, version 3 du 31 juillet 2017.