Académique Documents
Professionnel Documents
Culture Documents
NET
Courses
1
4.4.4 DELETE ................................................................................................................................. 28
2
7.6.3 Ajout de contraintes existantes à un DataSet ............................................................... 70
7.6.4 Utilisation des paramètres avec un DataAdapter ........................................................ 71
3
L’objectif est de maîtriser les technologies ADO.NET et développer des applications
réseaux professionnelles.
4
1 Présentation d’ADO.NET et du Framework .NET
5
1.1 ADO.NET
ADO.NET est une technologie d'accès aux données de Microsoft .NET Framework qui
assure la communication entre systèmes relationnels et non relationnels via un
ensemble commun de composants. ADO.NET est donc un ensemble de composants
présents de base dans le framework .NET permettant l’accès et la gestion de données
situées sur une base de données relationnelle (SQL Server, Oracle, etc…) ou non.
ADO.NET est une évolution de ADO (ActiveX Data Objects).
Les classes ADO.NET peuvent être divisées en 2 parties. Les classes permettant de se
connecter à la source de données et les classes utilisées pour gérer les données.
Les composants ADO (pour ActiveX Data Objects) renvoient à une technologie
Microsoft d'accès à des sources de données externes. L'idée première est d'offrir une
interface unifiée pour permettre à n'importe quel langage de programmation
d'accéder à OLE DB, l'API d'accès uniforme à des bases de données (relationnelles ou
non, SQL ou non). Pour des requêtes simples, ADO ne nécessite pas de connaissances
SQL.
Pour commencer, là où ADO offrait un accès aux données via les composants COM,
ADO.Net ne fonctionne que pour le système .Net, en se basant fortement sur XML et les
données d'applications. ADO.Net profite également des leçons tirées d'ADO en
matière d'interopérabilité et de tenue de charge.
Mais le fonctionnement même d'ADO a été revu pour son passage à .Net. Grâce à
l'introduction d'un nouvel objet standard, le DataSet, chargé de créer une
représentation en mémoire des données relationnelles, ADO.Net peut utiliser les
données de manière déconnectée, là où ADO devait constamment rester connecté à
la source. Le DataSet stocke sous forme XML les données nécessaires au traitement en
6
cours, et n'accède à la source qu'en cas de besoin (chargement, sauvegarde,
modification). Cet objet remplace donc avantageusement le RecordSet d'ADO.
Les fonctionnalités de RecordSet ont été réparties au sein de quatre objets principaux :
DataSet pour la représentation en mémoire ; DataReader pour la lecture rapide des
données ; DataAdapter pour réaliser la liaison entre DataSet et la source ; et
Command, qui regroupe toutes les fonctionnalités de traitement explicite, notamment
l'exécution de requêtes.
Pour résumer : les différences entre ADO et ADO.Net sont notables. Principale méthode
d'accès aux données de l'univers Microsoft, l'apprentissage d'ADO.Net devient
obligatoire si l'on veut être prêt pour la prochaine évolution du mode de
développement Windows, basé sur .Net.
Si vous utilisez Windows depuis très longtemps, vous avez probablement déjà entendu
parler de Microsoft. NET, probablement parce qu'une application vous a demandé de
l'installer, ou vous l'avez remarqué dans votre liste de programmes installés. Sauf si vous
êtes un développeur, vous n'avez pas besoin de beaucoup de connaissances pour
l'utiliser. Vous en avez juste besoin pour travailler. Mais, puisque nous connaissons les
7
choses, rejoignez-nous en explorant ce qu'est .NET et pourquoi tant d'applications en
ont besoin.
Le nom ".NET Framework" lui-même est un peu d'un abus de langage. Un framework (en
termes de programmation) est vraiment une collection d'interfaces de programmation
d'application (API) et une bibliothèque de code partagée que les développeurs
peuvent appeler lors du développement d'applications, afin qu'ils n'aient pas à écrire
le code rayure. Dans le .NET Framework, cette bibliothèque de code partagé est
appelée la bibliothèque de classes de structure (FCL). Les bits de code dans la
bibliothèque partagée peuvent effectuer toutes sortes de fonctions différentes.
Supposons, par exemple, qu'un développeur ait besoin de son application pour
pouvoir envoyer une requête ping à une autre adresse IP sur le réseau. Au lieu d'écrire
ce code eux-mêmes, puis d'écrire tous les petits morceaux qui doivent interpréter ce
que signifient les résultats du ping, ils peuvent utiliser du code de la bibliothèque qui
exécute cette fonction.
Et ce n'est qu'un petit exemple. Le .NET Framework contient des dizaines de milliers de
morceaux de code partagés. Ce code partagé rend la vie des développeurs
beaucoup plus facile car ils n'ont pas à réinventer la roue chaque fois que leurs
applications ont besoin d'effectuer une fonction commune. Au lieu de cela, ils peuvent
se concentrer sur le code propre à leurs applications et sur l'interface utilisateur qui les
relie tous. L'utilisation d'un framework de code partagé comme celui-ci permet
également de fournir des standards entre les applications. D'autres développeurs
peuvent donner un sens à ce qu'un programme fait plus facilement et les utilisateurs
des applications peuvent compter sur des choses comme les boîtes de dialogue Ouvrir
et Enregistrer sous qui fonctionnent de la même manière dans différentes applications.
8
Fournir un environnement cohérent de programmation orientée objet que le
code objet soit stocké et exécuté localement, exécuté localement mais
distribué sur Internet ou exécuté à distance.
Générer toutes les communications à partir des normes d'industries pour s'assurer
que le code basé sur le Framework .NET peut s'intégrer à n'importe quel autre
code.
9
plusieurs sources de données. Pour plus d'informations sur le modèle général ADO.NET,
voir la documentation du SDK Microsoft .NET Framework.
.NET Framework propose des fournisseurs pour MS SQL, OLE DB et Oracle. Outre la
gestion des fournisseurs .NET, ce produit comprend AdoDbx Client et BDP.NET. Ces
fournisseurs de données se connectent à de nombreuses bases de données les plus
courantes du marché, en proposant un environnement de programmation cohérent.
10
Pour plus d'informations, voir la rubrique Fournisseurs de données Borland pour Microsoft
.NET.
Remarque: BDP.NET est basé sur ADO.NET 1.1. AdoDbx Client est basé sur .NET 2.0.
L'objet ensemble de données représente en mémoire les tables et les relations d’une
ou plusieurs sources de données. L'ensemble de données offre une zone de travail
temporaire pour manipuler les données. Les applications ADO.NET manipulent les
tables en mémoire, pas dans la base de données physique. L'ensemble de données
offre davantage de flexibilité que les connexions directes avec les bases de données.
A l'instar d'un objet « ensemble de données » typique géré par de nombreux systèmes
de bases de données, l'ensemble de données peut contenir plusieurs tables de
données, lesquelles sont des représentations de tables ou de vues provenant de
nombreuses sources de données. L'ensemble de données travaille en mode
asynchrone, non connecté, et transmet ultérieurement des commandes d'actualisation
à la source de données via le fournisseur.
ADO.NET assure l'accès aux données pour divers modèles de programmation .NET.
En ASP.NET les Web Forms constituent une interface commode pour accéder à des
bases de données via le Web. ASP.NET utilise ADO.NET pour assurer les fonctions
d'accès aux données.
11
1.5 Espace de nommage
C://Windows
C:\Windows\Microsoft.NET\Framework
Regedit
1. Dans le menu Démarrer, choisissez Exécuter.
2. Dans la zone Ouvrir (ou "Rechercher les programmes et fichiers"), entrez
regedit.exe.
3. Vous devez être identifié sous un compte administratif pour pouvoir exécuter
regedit.exe.
12
4. Lancez l'exécution de regedit.exe.
5. Dans l'Éditeur du Registre, ouvrez la sous-clé suivante :
6. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP
7. Les versions installées sont répertoriées sous la sous-clé NDP.
8. Le numéro de version est stocké dans l'entrée Version.
9. Pour .NET Framework 4, l'entrée Version se trouve sous la sous-clé Client ou Full
(sous NDP), ou sous les deux sous-clés.
13
2 Evolution des techniques d’accès aux données
14
Depuis la sortie de .NET Microsoft n'arrête plus sa course folle ! La plupart des
technologies publiées faisaient partie d'un plan presque totalement connu à l'avance,
comme WPF, WCF etc. Il a fallu du temps pour qu'émerge ses "modules" de .NET car
même le plus puissant des éditeurs de logiciels du monde ne peut pas releaser la
totalité d'une montagne comme .NET en un seul morceau. S'il était clair que WPF serait
la nouvelle gestion des interfaces utilisateurs et que Windows Forms n'était bientôt
qu’un vieil outil, le foisonnement des technologies tournant autour des données n'était
pas forcément visible ni prévisible il y a quelques années. Et même aujourd'hui savoir ce
qui existe et comment s'en servir avec l'ensemble des autres technologies n'est pas
forcément une mince affaire !
Un petit dessin valant tous les discours, voici un diagramme qui tourne sur les blogs
américains de Microsoft et que j'ai en partie traduit pour vous, en espérant que cela
vous aidera à y voir plus clair !
15
ASP.NET MVC : Nouvelle couche permettant d'appliquer le paradigme Modèle-
Vue-Controleur aux développement ASP.NET
Les composants ADO (pour ActiveX Data Objects) renvoient à une technologie
Microsoft d'accès à des sources de données externes. L'idée première est d'offrir une
interface unifiée pour permettre à n'importe quel langage de programmation
d'accéder à OLE DB, l'API d'accès uniforme à des bases de données (relationnelles ou
non, SQL ou non). Pour des requêtes simples, ADO ne nécessite pas de connaissances
SQL.
Pour commencer, là où ADO offrait un accès aux données via les composants COM,
ADO.Net ne fonctionne que pour le système .Net, en se basant fortement sur XML et les
données d'applications. ADO.Net profite également des leçons tirées d'ADO en
matière d'interopérabilité et de tenue de charge.
16
3 Architecture globale de ADO.NET
17
18
4 Les modes d’accès aux données
19
Le framework .NET permet de communiquer avec une base de données de deux
manières: connectée ou non connectée.
3
using (SqlConnection conn = new SqlConnection(connectionString))
4 {
5 conn.Open();
6 }
• Utiliser un ConnectionStringBuilder
1 var connectionStringBuilder = new SqlConnectionStringBuilder();
2 connectionStringBuilder.DataSource = "localhost";
3 connectionStringBuilder.UserID = "admin";
connectionStringBuilder.Password = "toto";
4
5
using (SqlConnection conn = new
6 SqlConnection(connectionStringBuilder.ToString()))
7 {
8 conn.Open();
9 }
20
• Utiliser un fichier de configuration externe
string connectionString =
1System.Configuration.ConfigurationManager.ConnectionStrings["conString"].Conn
ectionString;
2
3
using (SqlConnection connection = new SqlConnection(connectionString))
4
{
5
connection.Open();
6
}
Pour lire les données, nous avons besoin d’une Command et d’un DataReader. La
Command représente le requête SQL de sélection des données. Le Datareader stocke
les résultats de notre requête. Il nous permet de lire les lignes retournées par une
21
commande. Il va uniquement de l’avant, une fois qu’une ligne est dépassée impossible
d’y revenir.
1 string connectionString =
System.Configuration.ConfigurationManager.ConnectionStrings["conString"].Con
2 nectionString;
8 while (sqlReader.Read())
9 {
1 Console.WriteLine($"{sqlReader["LastName"]} {sqlReader["FirstName"]}
{Convert.ToDateTime(sqlReader["BirthDate"]).ToString("dd/MM/yyyy")}");
0
}
1
1 sqlReader.Close();
1 sqlCmd.Dispose();
2}
Pour mettre à jour les données avec une requête update, delete, insert on utilise une
commande également. Mais ExecuteNonQuery est utilisée à la place de
ExecuteReader. ExecuteNonQuery renvoie le nombre de lignes impactées.
1 string connectionString =
System.Configuration.ConfigurationManager.ConnectionStrings["conString"].ConnectionString
2
3
using (SqlConnection connection = new SqlConnection(connectionString))
4 {
5 connection.Open();
22
7 SqlCommand sqlCmd = new SqlCommand("UPDATE Employees SET Region = 'NY' WHERE Region I
NULL", connection);
8
9
int nbLignesModifies = sqlCmd.ExecuteNonQuery();
10
11
Console.WriteLine($"Nb lignes modifiees = {nbLignesModifies}");
12}
Pour se connecter à la base, on aura besoin d’un objet Connection qui est créé à
partir d’une connection string qui contient toutes les infos nécessaires à la connexion à
la base: adresse, login, mot de passe…
4.3.1.1 la connexion
La connexion SqlConnection désigne un canal par lequel sont échangés les ordres et
les lignes SQL. Ce canal relie le programme C# et la base de données. L'objet
SqlConnection possède plusieurs états, dont deux remarquables : ouvert et fermé. Les
autres états sont actifs en régime transitoire ou en cas d'erreur.
23
Le programme interagit avec une connexion par le biais de la propriété
ConnectionString et des méthodes Open(), Close(). L'essentiel des opérations liées à
une base ne peut se faire que sur une connexion ouverte
La syntaxe try... catch... finally est alors la seule construction possible pour être certain
de fermer une connexion et de libérer des ressources acquises depuis l'ouverture :
24
4.3.1.2 Authentification et chaîne de connexions
25
n'est pas activée, doivent figurer dans la chaîne de connexions les segments user id=xxx
et password=xxx.
4.4.1 SELECT
Faire un Select en mode connecté est facile. On crée notre connexion, comme en
mode déconnecté.
1
2 try
{
3 conn.Open();
4 adapter.Fill(data, "Employees");
5 }
6 catch { }
finally
7
{
8 conn.Close();
9 }
10
26
Nos données sont dans le Dataset, on peut les utiliser sans avoir besoin de la connexion.
4.4.2 INSERT
1 adapter.InsertCommand = insertCommand;
1 int nb = adapter.Update(table);
4.4.3 UPDATE
27
"EmployeeID");
1 adapter.UpdateCommand = updateCommand;
1
DataTable table = data.Tables["Employees"];
2
3 // on sélectionne tous les villes dont le pays est UK
4 var rows = table.Select("Country = 'UK'");
5
6 // on modifie la ville à Manchester
7 foreach (DataRow row in rows)
{
8
row["City"] = "Manchester";
9 }
10
On met à jour nos données avec Update.
1 int nb = adapter.Update(table);
4.4.4 DELETE
1 adapter.DeleteCommand = deleteCommand;
On supprime les lignes. Dans l’exemple ci-dessous, on supprime tous les employés qui
ont pour nom Toto.
28
1 DataTable table = data.Tables["Employees"];
2
3 var rows = table.Select("LastName = 'toto'");
4
5 int n = rows.Length;
6
7 for(int i =0; i < n; i++)
8 {
rows[i].Delete();
9 }
10
On met à jour la base.
1
2 int nb = adapter.Update(table);
L’intérêt du Dataset est qu’il permet de stocker plusieurs tables et il propose plusieurs
fonctions pour sérialiser nos données. L’un des inconvénients du mode déconnecté est
qu’il peut y avoir un décalage entre les données en base et les données dans notre
Dataset.
29
5 Les fournisseurs d’accès aux données
30
Un fournisseur de données .NET Framework est utilisé pour la connexion à une base de
données, l'exécution de commandes et l'extraction de résultats. Ces résultats sont
traités directement, placés dans un objet DataSet pour pouvoir être exposés à
l'utilisateur le cas échéant, combinés aux données de différentes sources ou
accessibles à distance entre couches. Les fournisseurs de données .NET Framework sont
légers et créent une couche minimale entre la source des données et le code, ce qui
augmente les performances sans nuire aux fonctionnalités.
Le tableau suivant répertorie les fournisseurs de données inclus dans le .NET Framework.
Fournisseur de
données.NET Framework Description
.NET Framework Fournisseur de Fournit l'accès aux données pour Microsoft SQL
données pour SQL Server Server. Utilise l'espace de noms System.Data.SqlClient .
.NET Framework Fournisseur de Pour les sources de données exposées à l'aide de OLE
données pour OLE DB DB. Utilise l'espace de noms System.Data.OleDb .
fournisseur EntityClient Fournit un accès aux données pour les applications EDM
(Entity Data Model). Utilise l'espace de
noms System.Data.EntityClient .
.NET FrameworkFournisseur de Fournit l’accès aux données pour Microsoft SQL Server
données pour SQL Server Compact 4.0. Utilise l’espace de
Compact 4.0. noms System.Data.SqlServerCe .
31
5.1 Objets principaux des fournisseurs de données .NET Framework
Le tableau suivant présente les quatre principaux objets qui composent un fournisseur
de données .NET Framework .
Objet Description
Connection Établit une connexion à une source de données spécifique. La classe de base
pour tous les objets Connection est la classe DbConnection .
Command Exécute une commande sur une source de données. Expose Parameters et
peut exécuter dans la portée d'une Transaction à partir d'un Connection. La
classe de base pour tous les objets Command est la classe DbCommand .
DataReader Lit un flux de données avant uniquement et en lecture seule à partir d'une
source de données. La classe de base pour tous les objets DataReader est la
classe DbDataReader .
32
Objet Description
33
5.2 Fournisseur de données .NET Framework pour OLE DB
Le tableau suivant montre les fournisseurs qui ont été testés avec ADO.NET.
Pilote Fournisseur
Notes
Il est déconseillé d'utiliser une base de données Access (Jet) comme source de données pour
les applications multithread, telles que les applications ASP.NET . Si vous devez utiliser Jet comme
source de données pour une application ASP.NET , sachez que les applications ASP.NET qui se
connectent à une base de données Access peuvent rencontrer des problèmes de connexion.
Le fournisseur de données .NET Framework pour OLE DB ne prend pas en charge les interfaces
OLE DB version 2.5. Les fournisseurs OLE DB qui requièrent la prise en charge des interfaces OLE
DB 2.5 ne fonctionneront pas correctement avec le fournisseur de données .NET Framework pour
OLE DB. C'est le cas du fournisseur Microsoft OLE DB pour Exchange et du fournisseur Microsoft
OLE DB pour Internet Publishing.
Le fournisseur de données .NET Framework pour OLE DB ne fonctionne pas avec le fournisseur
OLE DB pour ODBC (MSDASQL). Pour accéder à une source de données ODBC à l'aide
de ADO.NET, utilisez le fournisseur de données .NET Framework pour ODBC.
34
Les classes du fournisseur de données.NET Framework pour OLE DB sont situées dans l'espace de
noms System.Data.OleDb . L'exemple de code suivant montre comment inclure l'espace de
noms System.Data.OleDb dans vos applications.
C#
using System.Data.OleDb;
Le tableau suivant indique les pilotes ODBC qui ont été testés avec ADO.NET.
Pilote
SQL Server
Les classes du fournisseur de données.NET Framework pour ODBC sont situées dans
l'espace de noms System.Data.Odbc .
C#
35
using System.Data.Odbc;
Notes
Le fournisseur de données .NET Framework pour ODBC requiert MDAC version 2.6 ou
ultérieure et MDAC 2.8 Service Pack 1 (SP1) est recommandé. Vous pouvez télécharger
MDAC 2.8 SP1 à partir du Data Access and Storage Developer Center.
Fournisseur Notes
36
Fournisseur Notes
37
6 Les objets du mode connecté
38
6.1 L'objet Connection :
L'ouverture d'une connexion est réalisée par la méthode Open et la fermeture par la
méthode Close.
Imports System
Imports System.Data.SqlClient
Imports System.IO
namespace ExempleAdoNetVBnet
try
oConnection.Open()
39
oConnection.Close()
catch e as Exception
End Try
End Sub
End Class
End Namespace
Une fois la connexion vers une base de données effectuée, vous pouvez exécuter une
requête et récupérer son résultat en utilisant l'objet Command. Contrairement au
fonctionnement des ADO où il était possible d'exécuter une requête sans utiliser l'objet
Command, vous devez désormais systématiquement le faire.
La création d'un objet Command nécessite l'instanciation d'un objet SqlCommand. Cet
objet expose différentes méthodes Execute à utiliser selon le résultat attendu :
40
La méthode ExecuteNonQuery exécute une commande ne retournant pas de lignes.
Imports System
Imports System.Data.SqlClient
Imports System.IO
Namespace ExempleAdoNetVBNET
Try
oConnection.Open()
oCommand.ExecuteNonQuery()
oConnection.Close()
Catch e As Exception
End Try
41
6.3 L'objet DataReader:
Avec un objet DataReader on extrait les données en lecture seule : une Requête SQL
(sur un objet command) charge le DataReader. C’est rapide ; on peut lire uniquement
les données et aller à l'enregistrement suivant. Il travaille en mode connecté.
L'objet DataReader ne stocke en mémoire qu'une seule ligne à la fois, permettant ainsi
d'augmenter les performances d'une application et d'en réduire la charge.
Comme l'objet DataReader a été conçu pour accéder aux données selon un mode
connecté, il ne peut être transmis entre différents tiers applicatifs ce que réalisait un
Recordset déconnecté
Par défaut, un DataReader charge une ligne entière en mémoire à chaque appel de
la méthode Read. Il est possible d'accéder aux valeurs de colonnes soit par leurs noms
soit par leurs références ordinales. Une solution plus performante est proposée
permettant d'accéder aux valeurs dans leurs types de données natifs (GetInt32,
GetDouble, GetString). Par exemple si la première colonne de la ligne indicée par 0 est
de type int, alors il est possible de la récupérer à l'aide de la méthode GetInt32 de
l'objet DataReader.
iColonne = oDataReader.GetInt32(0)
42
La méthode Close ferme un objet DataReader. Précisons que si l'objet Command utilisé
contient des paramètres en sortie ou des valeurs de retours, ils ne pourront être
récupérés qu'a l'issue de la fermeture du DataReader.
Après vous avoir présenté les principales méthodes de l'objet DataReader, regardons
plus précisément leurs mise en ouvre à l'aide d'un exemple d'extraction de données.
Imports System
Imports System.Data.SqlClient
Imports System.IO
Namespace ExempleAdoNetVBNET
Try
oConnection.Open()
Do
43
Console.WriteLine(ControlChars.Tab + "{0}" + ControlChars.Tab + "{1}",
oReader.GetName(0), oReader.GetName(1))
While oReader.Read()
End While
oReader.Close()
oConnection.Close()
Catch e As Exception
End Try
Voyons maintenant comment appeler une procédure stockée en utilisant les objets
Command et la collection Parameters.
Les procédures stockées sont un ensemble d'ordres SQL compilés sur un moteur SGBD.
Grâce aux procédures stockées, il est possible de centraliser l'ensemble du code SQL
de votre application à un seul endroit, ce qui en facile la maintenance. D'autre part,
une procédure stockée est plus performante qu'une requête dynamique dont le plan
d'exécution et la compilation doivent être effectués par le moteur SGBD avant toute
exécution.
44
'Définition du type de commande
oCmd.CommandType = CommandType.StoredProcedure
Description
Type
Exemple
Nom du paramètre
String @nom
Type de données
SqlDbType
SqlDbType.Char
Taille de la colonne
Int 25
string nom
oParam.Value = "John"
oParam.ParameterDirection = ParameterDirection.ReturnValue
Précisons :
qu'il est nécessaire d'affecter à la propriété Value de l'objet Parameter une valeur pour
les paramètres qui le nécessitent.
45
' Exemple d'appel à une procedure stockée
Imports System
Imports System.Data.SqlClient
Imports System.Data
Imports System.IO
Namespace ExempleAdoNetVBNET
Try
oCommand.CommandType = CommandType.StoredProcedure
oConnection.Open()
While oReader.Read()
oConnection.Close()
Catch e As Exception
46
Console.WriteLine(("L'erreur suivante a été rencontrée :" + e.Message))
End Try
47
7 Les objets du mode déconnecté
48
L'objet DataSet ADO.NET est une représentation de données résidente en mémoire qui
propose un modèle de programmation relationnel cohérent, quelle que soit la source
des données qu'il contient. Un objet DataSet représente un jeu de données complet, y
compris les tables qui contiennent et organisent les données et y appliquent des
contraintes, ainsi que les relations entre les tables.
L'utilisation d'un objet DataSet peut se faire via différentes méthodes qui peuvent être
appliquées indépendamment les unes des autres ou combinées. Plusieurs possibilités
s'offrent à vous :
Un objet DataSet fortement typé peut aussi être transporté au moyen d'un service Web
XML. Le design de l'objet DataSet le rend idéal pour le transport de données à l'aide
des services Web XML. Pour une vue d'ensemble des services Web XML, voir Vue
d'ensemble des services Web XML.
L’objet DataAdapter est le support du mode « déconnecté ». Il est plus élaboré que le
DataReader et contient quatre command s, correspondant aux quatre instructions SQL
de base : SELECT, UPDATE, INSERT, DELETE. Il permet de remplir le DataSet, grâce à sa
requête SELECT, et éventuellement de répercuter des mises à jour du DataSet vers la
base de données via les requêtes UPDATE, INSERT et DELETE.
49
Pour effectuer une modification, on modifie le DataSet puis on met à jour la base de
données à partir du DataSet. Pour remplir un DataSet il faut une Connexion puis un
DataAdapter.
Il faut créer un objet Connexion puis un objet DataAdapter qui par sa propriété Fill
charge le DataSet.
Pour mettre à jour la base après modification du DataSet il faut utiliser l’objet
CommandBuilder.
Pour créer une instance d'un objet DataSet, appelez le constructeur de l'objet DataSet.
Vous pouvez également spécifier un argument de nom. Si vous ne spécifiez pas de
nom pour l'objet DataSet, le nom est défini sur « NewDataSet ».
Vous pouvez également créer un objet DataSet à partir d'un objet DataSet existant. Le
nouvel objet DataSet peut être :
L'exemple de code suivant montre comment construire une instance d'un objet
DataSet.
ADO.NET vous permet de créer des objets DataTable et de les ajouter à un objet
DataSet existant. Vous pouvez définir des informations de contrainte pour un objet
DataTable en utilisant les propriétés PrimaryKey et Unique.
50
7.5.1 Exemple
L'exemple suivant construit un objet DataSet, ajoute un nouvel objet DataTable à l'objet
DataSet, puis ajoute trois objets DataColumn à la table. Enfin, ce code définit une
colonne en tant que colonne clé primaire.
Un objet DataSet peut contenir plusieurs tables ou relations dont les noms ne diffèrent
que par la casse. Dans ces cas, les références par nom aux tables et relations
respectent la casse. Par exemple, si l'objet DataSet dataSet contient les tables Table1
et table1, vous devez référencer Table1 par nom comme dataSet.Tables["Table1"] et
table1 comme dataSet.Tables ["table1"]. Si vous tentez de référencer l'une des tables
comme dataSet.Tables["TABLE1"], une exception sera levée.
51
Le comportement de respect de la casse ne s'applique pas s'il n'existe qu'une seule
table ou relation ayant un nom particulier. Par exemple, si l'objet DataSet ne comprend
que Table1, vous pouvez le référencer comme dataSet.Tables["TABLE1"].
Dans les versions antérieures d'ADO.NET, deux tables ne pouvaient pas porter le même
nom, même si elles se trouvaient dans des espaces de noms différents. Cette limitation
a été supprimée dans ADO.NET 2.0. Un objet DataSet peut contenir deux tables avec la
même valeur de propriété TableName mais des valeurs de propriété Namespace
différentes.
Dans un objet DataSet contenant plusieurs objets DataTable, vous pouvez utiliser des
objets DataRelation pour associer une table à une autre, pour vous déplacer dans les
tables et pour retourner les lignes enfants ou parentes d'une table associée.
Les arguments requis pour créer un DataRelation sont un nom pour le DataRelation et
un tableau d'une ou de plusieurs références d'objet DataColumn aux colonnes qui
serviront de colonnes parentes et enfants dans la relation. Une fois que vous avez créé
un DataRelation, vous pouvez l'utiliser pour vous déplacer entre les tables et extraire des
valeurs.
L'exemple de code suivant crée un DataRelation entre deux objets DataTable dans un
objet DataSet. Chaque objet DataTable contient une colonne nommée CustID qui sert
de lien entre les deux objets DataTable. L'exemple ajoute un seul DataRelation à la
collection Relations de l'objet DataSet. Le premier argument de l'exemple spécifie le
nom du DataRelation créé. Le deuxième définit le DataColumn parent et le troisième le
DataColumn enfant.
52
customerOrders.Relations.Add("CustOrders", _
customerOrders.Tables("Customers").Columns("CustID"), _
customerOrders.Tables("Orders").Columns("CustID"))
Un DataRelation est également assorti d'une propriété Nested qui, définie sur true,
imbrique les lignes de la table enfant dans la ligne associée de la table parente lorsque
ce contenu est écrit sous la forme d'éléments XML à l'aide de WriteXml.
L'une des principales fonctions d'un objet DataRelation est de permettre de passer d'un
objet DataTable à un autre à l'intérieur d'un objet DataSet. Cela vous permet d'extraire
tous les objets DataRow associés dans un DataTable lorsqu'un DataRow est fourni à
partir d'un DataTable associé. Par exemple, après avoir créé un DataRelation entre une
table de clients et une table de commandes, vous pouvez extraire toutes les lignes de
commandes pour une ligne de client donnée à l'aide de GetChildRows.
L'exemple de code suivant crée un DataRelation entre les tables Customers (clients) et
Orders (commandes) d'un DataSet et retourne toutes les commandes de chaque
client.
L'exemple suivant s'appuie sur le précédent, en créant des relations entre quatre tables
et en explorant ces relations. Comme dans l'exemple précédent, CustomerID lie la
table Customers à la table Orders. Pour chaque client de la table Customers, toutes les
53
lignes enfants de la table Orders sont déterminées, afin de retourner le nombre de
commandes passées par un client et la valeur OrderID de chaque commande.
54
// Add the new DataTable to the DataSet.
dataSet.Tables.Add(table);
55
dataSet.Tables.Add(table);
56
L'exemple enrichi retourne aussi les valeurs des tables OrderDetails et Products. La table
Orders est associée à la table OrderDetails au moyen de OrderID afin de déterminer,
pour chaque client, les produits qui ont été commandés et en quelle quantité. La table
OrderDetails ne contenant que le ProductID d'un produit commandé, une relation est
créée entre OrderDetails et Products, au moyen de ProductID, afin de retourner le
ProductName. Dans cette relation, Products est la table parente et OrderDetails la
table enfant. Ainsi, lors de l'itération sur la table OrderDetails, la méthode
GetParentRow est appelée pour extraire la valeur ProductName associée.
57
DataColumn lNameColumn = new DataColumn();
lNameColumn.DataType = System.Type.GetType("System.String");
lNameColumn.ColumnName = "LName";
namesTable.Columns.Add(lNameColumn);
Notez que lorsque le DataRelation est créé pour les tables Customers et Orders, aucune
valeur n'est spécifiée pour l'indicateur createConstraints (la valeur par défaut est true).
Cela suppose que toutes les lignes de la table Orders ont une valeur CustomerID qui
existe dans la table parente Customers. Si un CustomerID existe dans la table Orders,
mais pas dans la table Customers, un objet ForeignKeyConstraint entraîne la levée
d'une exception.
Lorsque la colonne enfant est susceptible de contenir des valeurs qui ne figurent pas
dans la colonne parente, attribuez à l'indicateur createConstraints la valeur false
lorsque vous ajoutez le DataRelation. Dans l'exemple, l'indicateur createConstraints a la
valeur false pour le DataRelation établi entre les tables Orders et OrderDetails.
L'application peut ainsi retourner tous les enregistrements de la table OrderDetails et
seulement un sous-ensemble de la table Orders sans qu'une exception runtime ne soit
levée. La sortie de l'exemple se présente comme suit.
Quantity: 6
Quantity: 4
58
Product: Outback Lager
Quantity: 6
Quantity: 3
L'exemple de code suivant est enrichi pour que soient retournées les valeurs des tables
OrderDetails et Products et seulement un sous-ensemble des enregistrements de la
table Orders.
Visual Basic
customerOrders.Relations.Add("CustOrders", _
customerOrders.Tables("Customers").Columns("CustomerID"), _
customerOrders.Tables("Orders").Columns("CustomerID"))
customerOrders.Relations.Add("OrderDetail", _
customerOrders.Tables("Orders").Columns("OrderID"), _
customerOrders.Tables("OrderDetails").Columns("OrderID"), False)
customerOrders.Relations.Add("OrderProducts", _
customerOrders.Tables("Products").Columns("ProductID"), _
customerOrders.Tables("OrderDetails").Columns("ProductID"))
59
Dim custRow, orderRow, detailRow As DataRow
orderRow("OrderDate").ToString())
detailRow.GetParentRow(orderProductRelation) _
("ProductName").ToString())
detailRow("Quantity").ToString())
Next
Next
Next
60
que pour répercuter dans la source de données les modifications apportées aux
données.
L'objet DataSet ADO.NET est une représentation résidente en mémoire de données, qui
propose un modèle de programmation relationnel cohérent indépendant de la source
de données. Le DataSet représente un jeu de données complet, comprenant des
tables, des contraintes et des relations entre les tables. Étant donné que le DataSet est
indépendant de la source de données, il peut inclure des données locales par rapport
à l'application ainsi que des données provenant de plusieurs sources. L'interaction avec
les sources de données existantes est contrôlée par le DataAdapter.
La méthode Fill du DataAdapter est utilisée pour remplir un DataSet avec les résultats
de SelectCommand du DataAdapter. Fill prend comme arguments un DataSet à
remplir et un objet DataTable ou le nom du DataTable à remplir avec les lignes
retournées par SelectCommand.
La méthode Fill utilise l'objet DataReader de manière implicite pour retourner les noms
et les types de colonne utilisés pour créer les tables du DataSet ainsi que les données
pour remplir les lignes des tables du DataSet. Les tables et les colonnes ne sont créées
que si elles n'existent pas déjà ; sinon Fill utilise le schéma DataSet existant. Les types de
colonne sont créés comme des types .NET Framework conformément aux tables dans
Mappage des types de données du fournisseur .NET Framework aux types de données
.NET Framework. Les clés primaires ne sont pas créées sauf si elles existent dans la
source de données et que DataAdapter.MissingSchemaAction a la valeur
MissingSchemaAction.AddWithKey. Si Fill détecte qu'il existe une clé primaire pour une
table, il remplace les données du DataSet par celles de la source de données
provenant des lignes dont les valeurs de colonne de clé primaire correspondent à
61
celles de la ligne retournée par la source de données. Si aucune clé primaire n'est
trouvée, les données sont ajoutées aux tables du DataSet. Fill utilise tous les mappages
qui peuvent exister lors du remplissage du DataSet
L'exemple de code suivant crée une instance d'un objet SqlDataAdapter qui utilise un
objet SqlConnection à la base de données Northwind Microsoft SQL Server et remplit un
objet DataTable dans un DataSet avec la liste des clients. L'instruction SQL et les
arguments SqlConnection passés au constructeur SqlDataAdapter sont utilisés pour
créer la propriété SelectCommand de l'objet SqlDataAdapter.
Exemple
Un nombre indéfini d'objets DataAdapter peut être utilisé avec un DataSet. Chaque
DataAdapter peut être utilisé pour remplir un ou plusieurs objets DataTable et
répercuter les mises à jour dans la source de données correspondante. Les objets
DataRelation et Constraint peuvent être ajoutés localement au DataSet, ce qui vous
permet de relier les données provenant de différentes sources de données. Par
exemple, un DataSet peut contenir des données provenant d'une base de données
Microsoft SQL Server, d'une base de données IBM DB2 exposée via OLE DB et d'une
source de données qui diffuse le XML en continu. Un ou plusieurs objets DataAdapter
peuvent gérer la communication vers chaque source de données.
62
Exemple
Par défaut, le DataSet stocke les données à l'aide des types de données .NET
Framework. Pour la plupart des applications, ils fournissent une représentation pratique
des informations de la source de données. Cette représentation peut néanmoins poser
un problème lorsque le type de données dans la source de données est un decimal
SQL Server ou un type de données numérique. Le type de données decimal .NET
Framework autorise un maximum de 28 chiffres significatifs, tandis que le type de
données decimal SQL Server en autorise 38. Si SqlDataAdapter détermine, pendant
une opération Fill, que la précision d'un champ decimal SQL Server est supérieure à 28
caractères, la ligne actuelle n'est pas ajoutée au DataTable. Au lieu de cela,
l'événement FillError se produit, ce qui vous permet de déterminer une éventuelle perte
de précision et de répondre de manière appropriée. Pour obtenir la valeur decimal
63
SQL Server, vous pouvez également utiliser un objet SqlDataReader ou appeler la
méthode GetSqlDecimal.
Avant que les tables du DataSet ne soient remplies avec les données des colonnes
chapitres, une relation est créée entre les tables parent et enfant du jeu de lignes
hiérarchique par l'ajout d'une colonne entier aux deux tables, la définition d'auto-
incrémentation de la colonne parent et la création d'un DataRelation à l'aide des
colonnes ajoutées des deux tables. La relation ajoutée est nommée à l'aide des noms
de table parent et de colonne chapitre sous la forme «
ParentTableNameChapterColumnName ».
Notez que la colonne associée n'existe que dans le DataSet. Les remplissages ultérieurs
à partir de la source de données se traduisent par l'ajout de nouvelles lignes aux tables
plutôt que la fusion des modifications dans les lignes existantes.
Notez par ailleurs que si vous utilisez la surcharge DataAdapter.Fill qui prend un
DataTable, seule cette table sera remplie. Une colonne entier auto-incrémentée sera
toujours ajoutée à la table mais aucune table enfant ne sera créée ni remplie et
aucune relation ne sera créée.
64
L'exemple suivant utilise le fournisseur MSDataShape pour générer une colonne
chapitre de commandes pour chaque client d'une liste de clients. Un DataSet est alors
rempli avec les données.
Une fois l'opération Fill terminée, le DataSet contient deux tables : Customers et
CustomersOrders, où CustomersOrders représente la colonne chapitre. Une colonne
supplémentaire nommée Orders est ajoutée à la table Customers et une colonne
supplémentaire nommée CustomersOrders est ajoutée à la table CustomersOrders.
L'auto-incrémentation de la colonne Orders dans la table Customers est définie. Un
DataRelation, CustomersOrders, est créé avec les colonnes ajoutées aux tables,
Customers étant la table parente. Les tableaux suivants présentent certains résultats de
l'exemple.
TableName : Customers
TableName : CustomersOrders
ALFKI 10643 0
65
ALFKI 10692 0
ANATR 10308 1
ANATR 10625 1
Les paramètres Command peuvent être utilisés pour spécifier les valeurs d'entrée et de
sortie pour une instruction SQL ou une procédure stockée pour chaque ligne modifiée
dans un DataSet.
Si votre DataTable mappe à une table de base de données unique ou est généré par
elle, vous pouvez tirer parti de l'objet DbCommandBuilder pour générer
automatiquement les DeleteCommand, InsertCommand et UpdateCommand du
DataAdapter.
66
La méthode Update répercute vos modifications dans la source de données,
cependant d'autres clients peuvent avoir modifié les données au niveau de la source
depuis la dernière fois où vous avez rempli le DataSet. Pour actualiser votre DataSet
avec les données en cours, utilisez le DataAdapter et la méthode Fill. De nouvelles
lignes seront ajoutées à la table et les informations mises à jour seront incorporées dans
les lignes existantes. La méthode Fill détermine si une nouvelle ligne sera ajoutée ou une
ligne existante mise à jour en se fondant sur les valeurs de clé primaire des lignes du
DataSet et des lignes retournées par SelectCommand. Si une valeur de clé primaire
d'une ligne du DataSet correspond à celle d'une ligne des résultats retournés par
SelectCommand, la méthode Fill met à jour la ligne existante en y insérant les
informations de la ligne retournée par SelectCommand et définit pour la propriété
RowState de la ligne existante la valeur Unchanged. Si la valeur de clé primaire d'une
ligne retournée par SelectCommand n'a pas de correspondance dans les lignes du
DataSet, la méthode Fill ajoute une nouvelle ligne avec un RowState ayant pour valeur
Unchanged.
Pour gérer les exceptions qui peuvent se produire à l'appel de la méthode Update,
utilisez l'événement RowUpdated pour répondre aux erreurs de mise à jour des lignes.
Vous pouvez aussi affecter true à DataAdapter.ContinueUpdateOnError avant
d'appeler Update et répondre aux informations d'erreur stockées dans la propriété
RowError d'une ligne particulière lorsque la mise à jour est terminée.
Exemple
Les exemples suivants illustrent l'exécution des mises à jour des lignes modifiées en
définissant de manière explicite le UpdateCommand du DataAdapter. Notez que le
paramètre spécifié dans la clause WHERE de l'instruction UPDATE est défini pour utiliser
la valeur Original de SourceColumn. Cela est important car la valeur Current peut avoir
été modifiée et ne pas correspondre à la valeur dans la source de données. La valeur
Original est la valeur qui a été utilisée pour remplir le DataTable à partir de la source de
données.
67
adapter.UpdateCommand = New SqlCommand( _
adapter.UpdateCommand.Parameters.Add( _
"@CategoryID", SqlDbType.Int)
parameter.SourceColumn = "CategoryID"
parameter.SourceVersion = DataRowVersion.Original
adapter.Fill(dataSet, "Categories")
adapter.Update(dataSet, "Categories")
Si les tables de votre source de données ont des colonnes qui s'auto-incrémentent,
vous pouvez remplir les colonnes de votre DataSet soit en retournant la valeur d'auto-
incrémentation comme paramètre de sortie d'une procédure stockée et en la
mappant à une colonne dans une table, soit en utilisant l'événement RowUpdated du
DataAdapter.
Cependant, les valeurs de votre DataSet peuvent être désynchronisées par rapport aux
valeurs de la source de données et résulter en un comportement imprévisible. Prenons
par exemple une table avec une colonne de clé primaire auto-incrémentée de
CustomerID. Si vous ajoutez deux nouveaux clients dans le DataSet, ils reçoivent les
valeurs CustomerId auto-incrémentées de 1 et 2. Lorsque la ligne du deuxième client
est passée à la méthode Update du DataAdapter, la ligne nouvellement ajoutée reçoit
une valeur CustomerID auto-incrémentée de 1 au niveau de la source de données, qui
68
ne correspond pas à la valeur 2 du DataSet. Lorsque le DataAdapter remplit la ligne
dans le DataSet avec la valeur retournée, il y a violation de contrainte car la ligne du
premier client a déjà un CustomerID de 1.
Pour éviter ce comportement, lorsque vous travaillez avec des colonnes auto-
incrémentées au niveau d'une source de données et dans un DataSet, il est
recommandé de créer la colonne dans le DataSet avec un AutoIncrementStep de -1
et un AutoIncrementSeed de 0, et de vous assurer que votre source de données
génère des valeurs d'identité auto-incrémentées commençant par 1 et dont la valeur
de pas est positive. En conséquence, le DataSet générera pour les valeurs auto-
incrémentées des nombres négatifs qui ne sont pas en conflit avec les valeurs auto-
incrémentées positives générées par la source de données. Une autre solution consiste
à utiliser les colonnes de type Guid au lieu des colonnes auto-incrémentées.
L'algorithme qui génère les valeurs Guid ne doit jamais générer le même Guid dans le
DataSet que celui généré par la source de données. Ordre des insertions, mises à jour
et suppressions
Exemple
Le code suivant garantit, par exemple, que seront d'abord traitées les lignes supprimées
de la table puis les lignes mises à jour et enfin les lignes insérées.
69
dataSet.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Deleted))
adapter.Update(table.Select(Nothing, Nothing, _
DataViewRowState.ModifiedCurrent))
dataAdpater.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Added))
70
moment du design, il est recommandé de spécifier explicitement la ou les colonnes de
clé primaire afin d'atteindre une performance optimale. L'exemple de code suivant
montre comment ajouter des informations de schéma à un DataSet à l'aide de
FillSchema.
custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
custAdapter.Fill(custDataSet, "Customers")
Le DataAdapter a quatre propriétés qui sont utilisées pour extraire des données d'une
source de données et les mettre à jour : la propriété SelectCommand retourne des
données de la source de données ; les propriétés InsertCommand, UpdateCommand
et DeleteCommand sont utilisées pour gérer les modifications apportées à la source de
données. La propriété SelectCommand doit être définie avant d'appeler la méthode
Fill du DataAdapter. La propriété InsertCommand, UpdateCommand ou
DeleteCommand doit être définie avant que la méthode Update du DataAdapter ne
soit appelée, en fonction des modifications qui ont été apportées aux données dans
l'objet DataSet. Par exemple, si des lignes ont été ajoutées, InsertCommand doit être
défini avant d'appeler Update. Lorsque Update traite une ligne insérée, mise à jour ou
71
supprimée, le DataAdapter utilise la propriété Command respective pour traiter
l'action. Les informations actuelles concernant la ligne modifiée sont passées à l'objet
Command par l'intermédiaire de la collection Parameters.
Lors de la mise à jour d'une ligne au niveau de la source de données, vous appelez
l'instruction UPDATE, qui utilise un identificateur unique pour identifier la ligne dans la
table à mettre à jour. L'identificateur unique est généralement la valeur d'un champ de
clé primaire. L'instruction UPDATE utilise les paramètres qui contiennent l'identificateur
unique ainsi que les colonnes et les valeurs à mettre à jour, comme indiqué dans
l'instruction Transact-SQL suivante.
Dans cet exemple Visual Basic, le champ CompanyName est mis à jour avec la valeur
du paramètre @CompanyName pour la ligne où CustomerID a la valeur du paramètre
@CustomerID. Les paramètres extraient les informations de la ligne modifiée à l'aide de
la propriété SourceColumn de l'objet SqlParameter. Suivent les paramètres pour
l'instruction UPDATE précédemment donnée en exemple. Le code est basé sur
l'hypothèse que la variable adapter représente un objet SqlDataAdapter valide.
72
a la valeur Original. Cela garantit que la ligne existante dans la source de données est
mise à jour si la valeur de la ou des colonnes d'identification ont été changées dans le
DataRow modifié. Dans ce cas, la valeur de ligne Original correspondrait à la valeur
actuelle de la source de données et la valeur de ligne Current contiendrait la valeur
mise à jour. Le SourceVersion du paramètre @CompanyName n'est pas défini et
utilisera la valeur de ligne Current par défaut.
Suivent des exemples qui illustrent les instructions SQL à utiliser comme CommandText
pour les propriétés SelectCommand, InsertCommand, UpdateCommand et
DeleteCommand de l'objet SqlDataAdapter. Pour l'objet SqlDataAdapter, vous devez
utiliser les paramètres nommés.
Dim selectSQL As String = _ "SELECT CustomerID, CompanyName FROM Customers " & _
"WHERE CountryRegion = @CountryRegion AND City = @City" Dim insertSQL As String = _
"INSERT INTO Customers (CustomerID, CompanyName) " & _ "VALUES (@CustomerID,
@CompanyName)" Dim updateSQL As String = _ "UPDATE Customers SET CustomerID =
@CustomerID, & _ "CompanyName = @CompanyName " & _ "WHERE CustomerID =
@OldCustomerID" Dim deleteSQL As String = _ "DELETE FROM Customers WHERE
CustomerID = @CustomerID"
73
8 Travaux Pratiques : Faire une étude de cas par Etape
74
75