X. Vers ADO.NET▲
Avec la nouvelle plate-forme .NET, Microsoft fournit une nouvelle architecture d'accès aux données appelée ADO.NET. Rassurez-vous tout de suite, il n'y a rien de révolutionnaire dans celle-ci et la plupart des concepts vus dans cet article restent fondamentalement les mêmes.
Nous allons parcourir les modifications engendrées par cette architecture vis-à-vis d'ADO.
N.B : Pour plus de renseignements sur les objets ADO.NET je vous invite à lire Les objets Connection, Command et Datareader dans ADO.NET
Les objets DataAdapter et DataSet dans ADO.NET par leduke
X-A. DataSet▲
C'est probablement la modification la plus significative d'ADO.NET. L'objet recordset d'ADO a été supprimé pour être remplacé par le DataSet. Disons-le tout de suite, le nouvel objet est autrement plus performant que le recordset qui souffre de plusieurs défauts, le principal étant d'utiliser un format propriétaire. L'objet DataSet stocke les données au format XML. Il permet la construction de schémas de données complexes avec des relations, plusieurs tables pouvant provenir de diverses tables, etc.
On peut le voir comme un recordset issu du DataShaping, encore plus évolué. À tout point de vue, l'objet DataSet dépasse ce qu'il était possible de faire avec un recordset sous l'aspect mise en forme des données.
Dans une certaine mesure, on peut voir l'objet Recordset comme un Objet DataTable de l'objet DataSet. Le code suivant permet de remplir l'objet DataTable.
Dim
strConnection As
String
=
"Data Source=localhost;Initial
Catalog=
Pubs;Integrated Security=
True
"
Dim
cnn1 As
SqlConnection =
New
SqlConnection
(
strConnection)
cnn1.Open
(
)
Dim
strSelect As
String
=
"SELECT * FROM Authors"
Dim
da As
New
SqlDataAdapter
(
strSelect, cnn1)
Dim
EqvRst As
New
DataTable
(
"Auteurs"
)
da.Fill
(
EqvRst)
Comme nous le voyons, pas de grandes différences avec ADO à l'exception de l'objet DataAdapter. Une autre nouveauté intéressante est l'arrivée de la méthode ExecuteScalar qui permet de ne pas créer un DataSet lorsque la requête ne renvoie qu'une valeur.
Je vous ai donc dit qu'il n'y avait pas de révolution et pourtant, impossible de retrouver les curseurs dans ADO.NET. Rassurez-vous (surtout si vous n'avez rien compris à ce présent cours) ils ont été englobés dans deux objets DataReader (côté serveur) et DataAdapter (côté client) que nous allons voir maintenant. C'est là l'autre grande nouveauté d'ADO.NET, mais elle soulève nettement moins mon enthousiasme que le DataSet.
X-B. DataReader▲
Cet objet est stricto sensu le curseur FireHose d'ADO. C'est dans ADO.NET le seul curseur serveur (connecté) disponible. Il garde les mêmes inconvénients que son homologue ADO, c'est-à-dire qu'il est exclusif sur la connexion qu'il utilise, qu'il ne connaît pas sa position absolue et qu'il ne permet de voir les mises à jour que sur les enregistrements non encore lus. Par contre il reste extrêmement rapide.
Comme son homologue ADO, il est fait pour faire de la lecture de données en un passage, typiquement pour le remplissage de contrôle visuel.
Il s'agit d'une grosse modification de stratégie quant à l'accès aux données. Il y a principalement deux raisons invoquées :
- Les applications Web demandent une connexion minimale à la source de données
- Il est plus performant d'utiliser les procédures stockées pour manipuler les données côté serveur.
Cet argumentaire est parfaitement recevable, mais à mon avis il ne prend pas le problème dans toute son ampleur.
Tout d'abord, la disparition des curseurs modifiables côté serveur entraîne la disparition de la gestion de l'accès concurrentiel par des mécanismes du SGBD. Cela va donc reporter une lourde charge sur le développeur. Ensuite il n'est pas toujours possible de stocker des procédures dans la base de données. Enfin et surtout, le fait de devoir travailler uniquement en mode déconnecté demande des utilisateurs autrement formés à la pratique de la programmation des bases de données. Nous allons voir cela dans des considérations sur l'objet DataAdapter.
X-C. DataAdapter▲
Voilà le nouvel enrobage du moteur de curseur client. Cet objet est défini comme étant l'intermédiaire entre la source de données et l'objet DataSet. Nous allons regarder en détail comment sont répercutées les modifications de l'objet DataSet vers la source.
Commençons par reprendre le schéma global de fonctionnement de l'objet. Il utilise une méthode Fill pour gérer le remplissage du DataSet. Cette méthode est paramétrable par l'intermédiaire de la méthode Select de l'objet DataTable. Dans l'autre sens, on peut soit utiliser l'objet CommandBuilder, censé être l'équivalent, du point de vue du fonctionnement, du moteur de curseur, soit utiliser les méthodes de l'objet DataTable pour construire sa propre logique d'action.
X-C-1. Construire sa logique d'action▲
Nous allons uniquement étudier le cas des requêtes UPDATE. Je fais un code similaire à l'appel ADO
Dim
strConn, strSQL As
String
strConn =
"Provider=SQLOLEDB;Data Source=(local);Initial
Catalog=
Pubs;Trusted_Connection=
Yes;"
strSQL =
"SELECT Au_Id, Author, [year born] FROM Authors WHERE Au_Id
=
8139
"
Dim
da As
New
OleDbDataAdapter
(
strSQL, strConn)
Dim
tblAut As
New
DataTable
(
"Auteurs"
)
da.Fill
(
tblAut)
'Je modifie une ligne comme dans l'exemple ADO
tblAut.Rows
(
1
)(
"Year born"
) =
CShort
(
tblAut.Rows
(
1
)(
"Year born"
)) +
1
'Envoi de la modification à la source.
Try
da.Update
(
tblAut)
Console.WriteLine
(
"Modification effectuée"
)
Catch ex As
Exception
Console.WriteLine
(
"Erreur lors de la modification :"
&
ex.Message
)
End
Try
Ce code va provoquer une erreur. En effet, pour utiliser le DataAdapter, je dois avoir explicité auparavant ma logique de modification. Pour cela je vais utiliser une commande paramétrée « générique » qui indiquera à l'objet DataAdapter comment construire la requête action correspondante.
Dim
strConn, strSQL As
String
Dim
cnn1 As
OleDbConnection
'Je crée une connexion, un DataAdapter et un DataSet
strConn =
"Provider=SQLOLEDB;Data Source=(local);Initial
Catalog=
Pubs;Trusted_Connection=
Yes;"
strSQL =
"SELECT Au_Id, Author, [year born] FROM Authors WHERE Au_Id
=
8139
"
cnn1 =
New
OleDbConnection
(
strConn)
Dim
da As
New
OleDbDataAdapter
(
strSQL, cnn1)
Dim
tblAut As
New
DataTable
(
"Auteurs"
)
da.Fill
(
tblAut)
'Je définis la commande Update que va utiliser le DataAdapter
strSQL =
"UPDATE Authors SET Au_ID = ?, Author = ?, [year born] = ?
WHERE Au_ID =
? ABD Author =
? AND
[year
born] =
?
Dim
cmdUpdate As
New
OleDbCommand
(
strSQL, cnn1)
'Définition des paramètres en liaison au DataSet
Dim
pcUpdate As
OleDbParameterCollection =
cmdUpdate.Parameters
Dim
MonParam As
OleDbParameter
MonParam =
pcUpdate.Add
(
"nAu_Id"
, OleDbType.Integer
)
MonParam.SourceColumn
=
"Au_Id"
MonParam.SourceVersion
=
DataRowVersion.Original
MonParam =
pcUpdate.Add
(
"nAuthor"
, OleDbType.VarChar
,50
)
MonParam.SourceColumn
=
"Author"
MonParam.SourceVersion
=
DataRowVersion.Original
MonParam =
pcUpdate.Add
(
"nYearBorn"
, OleDbType.SmallInt
)
MonParam.SourceColumn
=
"Year Born"
MonParam.SourceVersion
=
DataRowVersion.Original
'Je modifie une ligne comme dans l'exemple ADO
da.UpdateCommand
=
cmdUpdate
tblAut.Rows
(
1
)(
"Year born"
) =
CShort
(
tblAut.Rows
(
1
)(
"Year born"
)) +
1
'J'envoie la modification
da.Update
(
tblAut)
Étudions ce code pour comprendre le fonctionnement.
Je définis ma requête générique en utilisant le caractère '?' pour signaler les paramètres. Dans le cas de cet exemple, je travaille en verrouillage optimiste puisque tous les champs apparaissent comme critères de la requête. Ma requête attend donc six paramètres. Pourtant je ne vais lui en passer que trois, puisque ces paramètres sont liés tout comme les propriétés Value et OriginalValue dans ADO.
À partir du moment où ces paramètres sont définis, l'objet DataAdapter saura construire une requête Update en récupérant les valeurs dans les enregistrements de mon DataTable.
Il est à noter que je pourrais procéder de plusieurs façons différentes pour augmenter la souplesse de fonctionnement au prix d'un code plus important.
Globalement, le fonctionnement caché d'ADO a été remplacé par un code, à la charge du développeur. Les bénéfices de ce changement sont nombreux. Tout d'abord je sais exactement quelle requête va être construite. Ensuite, je gère ce comportement à la création du projet et non plus à l'exécution ce qui représente un gain de temps important. Enfin, il est alors possible de faire des modifications plus complexes puisque je ne suis pas obligé d'utiliser des valeurs du DataSet comme paramètre de ma commande, de plus les requêtes relationnelles peuvent être correctement décrites.
Il y a toutefois deux inconvénients à ce système.
- Le développeur doit connaître le schéma de la source bien mieux que son homologue ADO.
- Le développeur doit surtout avoir une bien meilleure connaissance du SQL et des SGBD.
En effet, les connaisseurs SQL ne seront pas sans remarquer qu'il y a une faute dans la déclaration de ma requête générique. Dans la définition de ma base, il est précisé que le champ « Year Born » peut être NULL. Si mon enregistrement avant modification ne contient pas de valeur dans ce champ, je vais passer un critère du type WHERE [year born] = NULL. Or on ne peut jamais faire de comparaison de NULL avec '='. Pour fonctionner correctement, mon code doit être.
strSQL =
"UPDATE Authors SET Au_ID = ?, Author = ?, [year born] = ?
WHERE Au_ID = ? ABD Author = ? AND ([year born] = ? OR ((? IS NULL)
AND ([year born] IS NULL)))
Néanmoins, les développeurs aguerris à la programmation des SGBD préfèreront cette nouvelle façon de procéder beaucoup plus claire et souple que la programmation du moteur de curseur client d'ADO.
X-C-2. Utiliser le CommandBuilder▲
C'est sensiblement l'équivalent du moteur de curseur. Je ne vais donc pas entrer dans les détails de son utilisation. Quelques remarques toutefois :
- Les requêtes créées sont de type optimiste strict (eq adCriteriaAllCols)
- Les modifications ne doivent concerner qu'une seule table
- La table doit contenir une clef primaire, celle-ci doit être présente dans la requête ayant rempli le DataSet
La présence de cet objet permet aux utilisateurs ayant une faible connaissance du SQL ou à ceux n'ayant pas d'informations sur le schéma de travailler comme avec ADO.