Vous êtes sur la page 1sur 76

[Date] ADO.

NET
Courses

Raissa Nouetsa [ MTNCameroon MFS - Douala


Head Office]
[COMPANY NAME] 0
TABLE DES MATIÈRES
1 Présentation d’ADO.NET et du Framework .NET ......................................... 5

1.1 ADO.NET ......................................................................................................................6

1.2 Net Framework ...........................................................................................................7

1.3 Architecture .Net ........................................................................................................9

1.3.1 Source de données .......................................................................................................... 10


1.3.2 Fournisseurs de données .................................................................................................. 10
1.3.3 Ensemble de données...................................................................................................... 11

1.4 Interfaces utilisateurs ADO.NET ................................................................................11

1.4.1 Web Forms ......................................................................................................................... 11


1.4.2 Windows Forms .................................................................................................................. 11

1.5 Espace de nommage ..............................................................................................12

1.5.1 Espace de nommage AdoDbx Client ............................................................................ 12


1.5.2 1.5.2- Espace de nommage BDP.NET ............................................................................. 12

2 Evolution des techniques d’accès aux données .................................... 14

3 Architecture globale de ADO.NET ............................................................ 17

4 Les modes d’accès aux données ............................................................. 19

4.1 Connection string .....................................................................................................20

4.2 Mode connecté ........................................................................................................21

4.2.1 Lecture des données ........................................................................................................ 21


4.2.2 Mise à jour ......................................................................................................................... 22

4.3 Connexion à la base de données ..........................................................................23

4.4 Le mode déconnecté ..............................................................................................26

4.4.1 SELECT ................................................................................................................................. 26


4.4.2 INSERT ................................................................................................................................. 27
4.4.3 UPDATE................................................................................................................................ 27

1
4.4.4 DELETE ................................................................................................................................. 28

5 Les fournisseurs d’accès aux données ..................................................... 30

5.1 Objets principaux des fournisseurs de données .NET Framework ........................32

5.2 Fournisseur de données .NET Framework pour OLE DB ..........................................34

5.3 Fournisseur de données .NET Framework pour ODBC ...........................................35

5.4 Choix d'un fournisseur de données .NET Framework .............................................36

6 Les objets du mode connecté .................................................................. 38

6.1 L'objet Connection : .................................................................................................39

6.2 L'objet Command: ....................................................................................................40

6.3 L'objet DataReader: ..................................................................................................42

6.4 L’objet DataAdapter : ...............................................................................................49

6.5 L'objet DataSet: .........................................................................................................49

6.6 L'objet CommandBuilder: ........................................................................................50

6.7 Comment appeler une procédure stockée ? ........................................................44

7 Les objets du mode déconnecté .............................................................. 48

7.1 Introduction .................................................................. Error! Bookmark not defined.

7.2 Création d'un DataSet ..............................................................................................50

7.3 Ajout d'un DataTable à un DataSet .........................................................................50

7.3.1 Exemple ............................................................................................................................. 51


7.3.2 Respect de la casse ......................................................................................................... 51
7.3.3 Prise en charge des espaces de noms .......................................................................... 52

7.4 Ajout d'une relation entre différentes tables ..........................................................52

7.5 Exploration d'une relation entre tables ...................................................................53

7.6 Utilisation d'un DataSet avec des données existantes ..........................................60

7.6.1 Remplissage d'un DataSet à partir d'un DataAdapter ................................................. 61


7.6.2 Mise à jour de sources de données avec des DataAdapters .................................... 66

2
7.6.3 Ajout de contraintes existantes à un DataSet ............................................................... 70
7.6.4 Utilisation des paramètres avec un DataAdapter ........................................................ 71

8 Travaux Pratiques : Faire une étude de cas par Etape .......................... 74

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.

Lors du passage de son environnement de développement standard de Win32 à .Net,


Microsoft a proposé ADO.Net, construit sur ADO mais présentant de nombreuses
différences - de la même manière que VB.Net est plus un cousin éloigné de Visual Basic
qu'un simple saut de version...

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.

Mode déconnecté, abandon de RecordSet pour de nouveaux objets et méthodes,


fonctionnement avec XML : tout cela modifie déjà considérablement la perception de
l'outil. Par ailleurs, ADO.Net introduit de nombreux concepts inconnus à ADO : copie
par lots, énumération des serveurs SQL actifs, traitement asynchrone, notifications
serveur...

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.

ADO.NET est l'environnement de programmation .NET permettant de concevoir des


applications de bases de données basées sur des formats de données natifs ou des
données XML (eXtensible Markup Language « langage de balisage extensible »).
ADO.NET est conçu comme un stockage de données dorsal pour tous les modèles de
programmation Microsoft .NET, y compris les Web Forms et les services Web. Utilisez
ADO.NET pour gérer les données dans .NET Framework.

1.2 Net Framework

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.

Le Framework .NET est une plate-forme informatique qui simplifie le développement


d'applications en proposant une approche unifiée pour la conception d'applications
Web et Windows.

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.

Fournir un environnement d'exécution de code qui minimise le déploiement de


logiciels et de conflits de versionning.

Fournir un environnement d'exécution de code qui garantit l'exécution sécurisée


de code y compris le code créé par un tiers d'un niveau de confiance moyen
ou un tiers inconnu.

Fournir un environnement d'exécution de code qui élimine les problèmes de


performance des environnements interprétés ou écrits en scripts.

Fournir au développeur un environnement cohérent entre une grande variété


de types d'applications comme les applications Windows et les applications
Web.

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.

Le Framework .NET contient deux composants principaux : Le Common Language


Runtime (CLR) et la bibliothèque de classes du Framework .NET. Il existe aussi des
implémentations libres de .NET, dont le projet Mono.

En ce qui concerne le développement d'applications mobiles, Microsoft a développé


une version light de son Framework appelé le Compact Framework .NET.

1.3 Architecture .Net

Les deux principaux composants de l'architecture ADO.NET sont le fournisseur de


données et l'ensemble de données. La source de données représente une base de
données physique ou un fichier XML, le fournisseur de données assure les connexions et
transmet des commandes, l'ensemble de données représente en mémoire une ou

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.

1.3.1 Source de données

La source de données est la base de données physique, locale ou distante, ou un


fichier XML. Dans la programmation de base de données traditionnelle, le développeur
manipule directement la source de données, ce qui nécessite souvent des interfaces
propriétaires complexes. Avec ADO.NET, le développeur de base de données utilise un
jeu de composants pour accéder à la source de données, pour exposer les données et
transmettre des commandes.

1.3.2 Fournisseurs de données

Les composants fournisseur de données assurent la connexion avec la base de


données physique ou les fichiers XML en masquant les détails de l'implémentation. Les
fournisseurs peuvent se connecter à une ou plusieurs sources de données, transmettre
des commandes et exposer les données aux ensembles de données.

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

1.3.3 Ensemble de données

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.

1.4 Interfaces utilisateurs ADO.NET

ADO.NET assure l'accès aux données pour divers modèles de programmation .NET.

1.4.1 Web Forms

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.

Les composants de connexion .NET, AdoDbx Client et BDP.NET facilitent l'intégration


entre les Web Forms et ADO.NET. Les contrôles DB Web gèrent à la fois les composants
ADO.NET, AdoDbx Client et BDP.NET, accélérant ainsi le développement des
applications web.

1.4.2 Windows Forms

Windows Forms n'est plus supporté.

11
1.5 Espace de nommage

1.5.1 Espace de nommage AdoDbx Client

Les classes AdoDbx Client se trouvent sous l'espace de nommage


Borland.Data.AdoDbxClientProvider.

1.5.2 1.5.2- Espace de nommage BDP.NET

Les classes BDP.NET se trouvent sous les espaces de nommage Borland.Data.

Espace de nommage Description

Contient des objets communs à tous les fournisseurs de données


Borland, y compris les classes d'erreur et d'exception, les
Borland.Data.Common énumérations de type de données, les options de fournisseur et les
interfaces pour concevoir vos propres classes commande, connexion
et curseur.

Contient les classes BDP.NET essentielles comme BdpCommand,


BdpConnection, BdpDataAdapter et d'autres qui permettent
Borland.Data.Provider
d'interagir avec des sources de données externes comme les serveurs
de bases de données Oracle, DB2, Interbase et MS SQL Server.

Contient les interfaces permettant de construire vos propres classes


Borland.Data.Schema de manipulation de schéma de base de données ainsi que divers
types et énumérateurs définissant des métadonnées.

Quelle version de .Net est installée sur mon ordinateur ?

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 !

Quelques précisions pour certains acronymes ou noms de technologies :

EDM : Entity Data Model - Modèles de données de l'Entity Framework (EF)

RDBMS : SGBD, une base de données

ASP.NET Dynamic Data : nouveau système de .NET 3.5 SP1 simplifiant et


améliorant la prise en charge des données dynamiques dans ASP.NET

15
ASP.NET MVC : Nouvelle couche permettant d'appliquer le paradigme Modèle-
Vue-Controleur aux développement ASP.NET

ADO.NET Data Services : couche de communication transformant un modèle


EDM en un service Web

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.

Lors du passage de son environnement de développement standard de Win32 à .Net,


Microsoft a proposé ADO.Net, construit sur ADO mais présentant de nombreuses
différences - de la même manière que VB.Net est plus un cousin éloigné de Visual Basic
qu'un simple saut de version...

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.

En mode connecté, on se connecte à la base et on exécute nos requêtes SQL


directement sur la base (lecture, création, mise à jour, suppression…) en utilisant les
objets Connection, Command et DataReader.

En mode déconnecté, on manipule des objets Dataset et Datatable qui représentent


la structure de la base en mémoire. On les modifie en mode déconnecté puis on
synchronise avec la base avec un DataAdapter.

4.1 Connection string

Vous pouvez créer vos connections string de plusieurs façons:

• Utiliser une chaîne ce caractères


1 string connectionString = @"Server=localhost;Database=NORTHWND;User
Id=admin;Password=toto;";
2

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

Créer un fichier de configuration app.config, par exemple, et ajouter la


configuration de votre Connection string

<?xml version="1.0" encoding="utf-8" ?>


1
<configuration>
2
<connectionStrings>
3
<add name="conString" providerName="System.Data.SqlClient"
4 connectionString="Server=localhost;Database=NORTHWND;User
Id=admin;Password=user;"/>
5
</connectionStrings>
6
</configuration>

Ensuite, se connecter à la base de la manière suivante

string connectionString =
1System.Configuration.ConfigurationManager.ConnectionStrings["conString"].Conn
ectionString;
2

3
using (SqlConnection connection = new SqlConnection(connectionString))
4
{
5
connection.Open();
6
}

Plus d’informations sur les connection strings connectionstrings.com

4.2 Mode connecté

4.2.1 Lecture des données

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;

3 using (SqlConnection connection = new SqlConnection(connectionString))


{
4
connection.Open();
5
SqlCommand sqlCmd = new SqlCommand("SELECT LastName, FirstName,
6 BirthDate FROM Employees", connection);

7 SqlDataReader sqlReader = sqlCmd.ExecuteReader();

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}

4.2.2 Mise à jour

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}

4.3 Connexion à la base de données

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…

Toutes les classes Connection hérite de DbConnection. La classe Connection de


SQLServer, par exemple est SqlConnection. 4.1 Le mode connecté

En mode connecté, tous les formats de base de données adoptent le même


fonctionnement. Nous l'illustrerons avec SQL Server, et pour passer à d'autres formats, il
n'y aura qu'à changer d'espace de noms et à modifier le préfixe de chaque classe. Le
tableau suivant donne la marche à suivre pour appliquer ces changements :

Les autres classes sont nommées sur le même principe.

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 chaîne de connexions est formée de différents segments indiquant le nom de la


machine abritant la base, le nom de la base, les crédits de l'utilisateur. La syntaxe
dépend de chaque format de base. Pour SQL Server, la chaîne de connexions
comprend les informations suivantes :

La documentation MSDN fournit les détails des commutateurs constituant la chaîne de


connexions. Le programmeur se doit d'être particulièrement attentif à la manipulation
de la connexion. Une fois ouverte, elle consomme des ressources systèmes et ne peut
pas être réouverte avant d'être fermée ; une connexion mal fermée représente ainsi un
danger pour l'intégrité et les performances du système.

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

SQL Server dispose de deux modes d'authentification. Le premier consiste en la


fourniture, à chaque connexion, d'un couple (utilisateur, mot de passe) vérifié dans une
table des utilisateurs. Cette approche très classique peut s'avérer délicate lorsque les
informations de connexion sont consignées dans un fichier de configuration textuel ou
lorsqu'elles sont transmises très souvent sur le réseau.

4.3.1.3 Autre mode d’authentification

Le deuxième mode d'authentification, appelé sécurité intégrée, n'utilise pas de nom


d'utilisateur et de mot de passe transmis sur le réseau. Le système Windows authentifie
le programme client (dans notre cas, ASP.NET) et transmet le jeton d'authentification à
SQL Server. Ce jeton est codé et d'une durée de vie limitée, ce qui augmente la
sécurité d'ensemble.

Lorsque la chaîne de connexions comprend le segment integrated security= true (ou


=sspi), la sécurité intégrée est activée. L'utilisateur ASP.NET doit être préalablement
autorisé par SQL Server à l'accès à la base cible. Dans les cas où la sécurité intégrée

25
n'est pas activée, doivent figurer dans la chaîne de connexions les segments user id=xxx
et password=xxx.

4.4 Le mode déconnecté

Dans ce mode, on se connecte à la base de donnée, on charge en mémoire les


données qui nous intéressent (dans un dataset), puis on ferme la connexion. Le dataset
est la représentation en mémoire des données de la base. On les traite (select, insert,
delete…). Après le traitement, on met à jour les données en base.

4.4.1 SELECT

Faire un Select en mode connecté est facile. On crée notre connexion, comme en
mode déconnecté.

1 string connectionString = @"Server=REVO-ONE-


RL85\SQLEXPRESS;Database=NORTHWND;User Id=monid;Password=monmotdepasse;";
2 SqlConnection conn = new SqlConnection(connectionString);

On crée un DataAdapter et un Dataset. Le DataAdapter stocke notre requête et le


Dataset contiendra le résultat de la requête.

SqlDataAdapter adapter = new SqlDataAdapter("SELECT LastName, FirstName,


1 BirthDate FROM Employees", conn);
2 DataSet data = new DataSet();

On ouvre notre connexion, on alimente le Dataset puis on ferme la connexion.

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.

1 foreach (DataRow row in data.Tables["Employees"].Rows)


{
2 Console.WriteLine($"{row["LastName"]} {row["FirstName"]}
3 {Convert.ToDateTime(row["BirthDate"]).ToString("dd/MM/yyyy")}");
4 }

4.4.2 INSERT

Pour ajouter des données, il faut définir la commande d’insertion.

SqlCommand insertCommand = new SqlCommand("INSERT INTO Employees (LastName,


FirstName, BirthDate) VALUES (@LastName, @FirstName, @BirthDate)", conn);
1 insertCommand.Parameters.Add("@LastName", SqlDbType.NVarChar, 30,
2 "LastName");
3 insertCommand.Parameters.Add("@FirstName", SqlDbType.NVarChar, 30,
"FirstName");
4
insertCommand.Parameters.Add("@BirthDate", SqlDbType.DateTime,0,
"BirthDate");

Affecter la commande d’insertion à notre dataadapter.

1 adapter.InsertCommand = insertCommand;

Ensuite, on crée la ligne à ajouter dans notre table.

1 DataTable table = data.Tables["Employees"];


2 DataRow row = table.NewRow();
3
4 row["LastName"] = "Jean";
5 row["FirstName"] = "Pierre";
row["BirthDate"] = new DateTime(1985, 12, 1);
6
table.Rows.Add(row);
7
Puis, on met à jour la base de données

1 int nb = adapter.Update(table);

Update retourne le nombre de lignes modifiées.

4.4.3 UPDATE

Le fonctionnement de la mise à jour est similaire à l’insertion. On crée notre


commande.

1 SqlCommand updateCommand = new SqlCommand("Update Employees SET City=@City


WHERE EmployeeID=@EmployeeID", conn);
2 updateCommand.Parameters.Add("@City", SqlDbType.NVarChar, 30, "City");
3 updateCommand.Parameters.Add("@EmployeeID", SqlDbType.Int, 4,

27
"EmployeeID");

On affecte à notre adapter la commande.

1 adapter.UpdateCommand = updateCommand;

On fait quelques modifications dans nos données.

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);

Pour chaque ligne modifiée, la requête de la commande update est exécutée.


Donc, si les lignes modifiées ont les employeID suivants 3, 5 ,10, les requêtes suivantes
seront exécutées:

1 Update Employees SET City=Manchester WHERE EmployeeID=3;


2 Update Employees SET City=Manchester WHERE EmployeeID=5;
3 Update Employees SET City=Manchester WHERE EmployeeID=10;

La variable nb, contient 3, le nombre de lignes modifiées.

4.4.4 DELETE

La suppression de données fonctionne comme l’insertion et la modification. On définit


notre commande.

SqlCommand deleteCommand = new SqlCommand("DELETE FROM Employees WHERE


1 EmployeeID=@EmployeeID", conn);
2 deleteCommand.Parameters.Add("@EmployeeID", SqlDbType.Int, 4,
"EmployeeID");

On affecte à notre DataAdapter la commande de suppression.

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);

La variable nb contient le nombre de lignes supprimées.

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 .

.NET Framework Fournisseur de Pour les sources de données exposées à l'aide de


données pour ODBC ODBC. Utilise l'espace de noms System.Data.Odbc .

.NET Framework Fournisseur de Pour les sources de données Oracle. Le fournisseur de


données pour Oracle données .NET Framework pour Oracle prend en charge le
logiciel client Oracle version 8.1.7 et ultérieure, et utilise
l'espace de noms System.Data.OracleClient .

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 .

DataAdapter Remplit un DataSet et répercute les mises à jour dans la source de


données. La classe de base pour tous les objets DataAdapter est la
classe DbDataAdapter .

En plus des principales classes répertoriées dans le tableau précédemment dans ce


document, un fournisseur de données .NET Framework contient également les classes
répertoriées dans le tableau suivant.

32
Objet Description

Inscrit des commandes dans des transactions au niveau de la


source de données. La classe de base pour tous les
Transaction objets Transaction est la classe DbTransaction . ADO.NET fournit
aussi la prise en charge pour les transactions à l'aide des classes
dans l'espace de noms System.Transactions .

Objet d'assistance qui génère automatiquement les propriétés


de commande d'un DataAdapter ou dérive les informations sur
les paramètres à partir d'une procédure stockée et remplit la
CommandBuilder
collection Parameters d'un objet Command. La classe de base
pour tous les objets CommandBuilder est la
classe DbCommandBuilder .

Objet d'assistance qui offre une manière simple de créer et de


gérer le contenu de chaînes de connexion utilisées par les
ConnectionStringBuilder objets Connection. La classe de base pour tous les
objets ConnectionStringBuilderest la
classe DbConnectionStringBuilder .

Définit les paramètres des valeurs d'entrée, de sortie et de retour


Parameter pour les commandes et les procédures stockées. La classe de
base pour tous les objets Parameter est la classe DbParameter .

Retourné en cas d'erreur au niveau de la source de données. En


cas d'erreur côté client, les fournisseurs de
Exception données .NET Framework lèvent une
exception .NET Framework. La classe de base pour tous les
objets Exception est la classe DbException .

Expose les informations provenant d'un avertissement ou d'une


Error
erreur retournée par une source de données.

Fourni pour les attributs de sécurité d'accès du code du


ClientPermission fournisseur de données .NET Framework . La classe de base pour
tous les objets ClientPermission est la classe DBDataPermission .

33
5.2 Fournisseur de données .NET Framework pour OLE DB

Le fournisseur de données .NET Framework pour OLE DB (OleDb) utilise un fournisseur


OLE DB natif par le biais de COM Interop pour permettre l'accès aux données. Le
fournisseur de données.NET Framework pour OLE DB prend en charge les transactions
locales et distribuées. Pour les transactions distribuées, le fournisseur de
données .NET Framework pour OLE DB s'inscrit automatiquement par défaut dans une
transaction et obtient des détails de transaction des services de composants
Windows. Pour plus d’informations, consultez Transactions et la concurrence.

Le tableau suivant montre les fournisseurs qui ont été testés avec ADO.NET.

Pilote Fournisseur

SQLOLEDB Fournisseur Microsoft OLE DB pour SQL Server

MSDAORA Fournisseur Microsoft OLE DB pour Oracle

Microsoft.Jet.OLEDB.4.0 Fournisseur OLE DB pour Microsoft Jet

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;

5.3 Fournisseur de données .NET Framework pour ODBC

Le fournisseur de données .NET Framework pour ODBC (Odbc) utilise le


gestionnaire de pilotes (DM) ODBC natif pour permettre l'accès aux données. Le
fournisseur de données pour ODBC prend en charge les transactions locales et
distribuées. Pour les transactions distribuées, le fournisseur de données ODBC
s’inscrit automatiquement par défaut dans une transaction et obtient des
détails de transaction des services de composants Windows. Pour plus
d’informations, consultez Transactions et la concurrence.

Le tableau suivant indique les pilotes ODBC qui ont été testés avec ADO.NET.

Pilote

SQL Server

Microsoft ODBC pour Oracle

Pilote Microsoft Access (*.mdb)

Les classes du fournisseur de données.NET Framework pour ODBC sont situées dans
l'espace de noms System.Data.Odbc .

L'exemple de code suivant montre comment inclure l'espace de


noms System.Data.Odbc dans vos applications.

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.

5.4 Choix d'un fournisseur de données .NET Framework

Selon le design et la source de données de votre application, le choix de votre


fournisseur de données .NET Framework peut améliorer les performances, les

fonctionnalités et l'intégrité de votre application. Le tableau suivant présente les


avantages et les limites de chaque fournisseur de données .NET Framework .

Fournisseur Notes

.NET Framework Recommandé pour les applications de couche intermédiaire


Fournisseur de données qui utilisent Microsoft SQL Server.
pour SQL Server
Recommandé pour les applications monocouches qui
utilisent Microsoft Database Engine (MSDE) ou SQL Server.

Recommandé dans l’utilisation du fournisseur OLE DB pour


SQL Server (SQLOLEDB) avec le .NET Framework fournisseur de
données pour OLE DB.

.NET Framework Pour SQL Server, le .NET Framework est recommandé de


Fournisseur de données fournisseur de données pour SQL Server au lieu de ce
pour OLE DB fournisseur.

Il est recommandé pour les applications monocouches qui


utilisent des bases de données Microsoft Access. L'utilisation
d'une base de données Access pour une application de
couche intermédiaire est déconseillée.

.NET Framework Recommandé pour les applications monocouches et de

36
Fournisseur Notes

Fournisseur de données couche intermédiaire qui utilisent des sources de données


pour ODBC ODBC.

.NET Framework Recommandé pour les applications monocouches et de


Fournisseur de données couche intermédiaire qui utilisent des sources de données
pour Oracle Oracle.

37
6 Les objets du mode connecté

38
6.1 L'objet Connection :

L’objet Connection permet de se connecter à une base de données en donnant le


nom du serveur, de la base, et le nom et le mot de passe d’un utilisateur connu du
SGDB.

La connectivité à SQLServer 2000 est assurée par l'objet SqlConnection de l'espace de


noms System.Data.SqlClient. Le framework .Net propose ainsi des objets de connexion
différents en fonction du type de fournisseur de données choisi. Par exemple vous
devrez utiliser l'objet OleDbConnection si votre fournisseur est un fournisseur OleDb.

L'ouverture d'une connexion est réalisée par la méthode Open et la fermeture par la
méthode Close.

' Exemple de gestion d'une connexion

Imports System

Imports System.Data.SqlClient

Imports System.IO

namespace ExempleAdoNetVBnet

public class SQLConnexion

public static void Main()

Dim strConnexion As String = "Data Source=localhost; Integrated Security=SSPI;" &


"Initial Catalog=Northwind"

try

Dim oConnection As SqlConnection = New SqlConnection(strConnexion)

oConnection.Open()

Console.WriteLine("Etat de la connexion : " & oConnection.State)

39
oConnection.Close()

catch e as Exception

Console.WriteLine("L'erreur suivante a été rencontrée :" & e.Message)

End Try

End Sub

End Class

End Namespace

6.2 L'objet Command:

L’objet Command permet d’envoyer à la base de données, des requêtes SQL


d’interrogation ou de modification, avec ou sans paramètres.

✓ Avec ExecuteNonQuery de l'objet Command on peut manipuler directement la BD


(UPDATE, INSERT, DELETE, CREATE, DROP..).

✓ Avec ExecuteScalar de l'objet Command on peut récupérer les résultats d'une


requête SQL qui contient une instruction COUNT (comptage) AVG (moyenne) MIN
(valeur minimum) MAX (valeur maximum) SUM (somme).

✓ Avec ExecuteReader de l'objet Command on peut d’exécuter des requêtes SQL de


type SELECT qui renvoient des lignes au poste client.

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 :

La méthode ExecuteReader peut être utilisée pour récupérer un jeu d'enregistrements


et retourne un objet DataReader.

La méthode ExecuteScalar récupère une valeur unitaire.

40
La méthode ExecuteNonQuery exécute une commande ne retournant pas de lignes.

'Exemple d'utilisation d'un objet Command

Imports System

Imports System.Data.SqlClient

Imports System.IO

Namespace ExempleAdoNetVBNET

Public Class CommandeSQL

Public Shared Sub Main()

Dim strConnexion As String = "Data Source=localhost; Integrated Security=SSPI;" +


"Initial Catalog=Northwind"

Dim strRequete As String = "INSERT INTO Region VALUES (5,'Sud')"

Try

Dim oConnection As New SqlConnection(strConnexion)

Dim oCommand As New SqlCommand(strRequete, oConnection)

oConnection.Open()

oCommand.ExecuteNonQuery()

oConnection.Close()

Catch e As Exception

Console.WriteLine(("L'erreur suivante a été rencontrée :" + e.Message))

End Try

End Sub 'Main

End Class 'CommandeSQL

End Namespace 'ExempleAdoNetVBNET

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é.

Il faut créer un objet Connexion puis un objet Command, ensuite on exécute la


propriété ExecuteReader pour créer l'objet DataReader; enfin on parcourt les
enregistrements avec la méthode Read.

L'objet DataReader permet de récupérer d'une source de données un flux en lecture


seule en avant seulement (read only, forward only). Il résulte de l'exécution de la
méthode ExecuteReader sur un objet Command.

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.

Il est recommandé d'utiliser cet objet si :

• Vous n'avez pas besoin de réaliser un cache des données


• Vous traitez un jeu d'enregistrements trop important pour être stocké en mémoire
• Vous souhaitez accéder à des données rapidement en lecture seule en avant
seulement

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.

Dim iColonne As Integer

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.

Pour augmenter les performances, il est parfois nécessaire de soumettre plusieurs


requêtes à la fois. L'objet DataReader répond à ce besoin avec la méthode NextResult
permettant de passer d'un jeu d'enregistrement à un autre. Pour les habitués des ADO,
cette méthode est équivalente au NextRecordset.

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.

Exemple d'extraction de données avec l'objet DataReader

Imports System

Imports System.Data.SqlClient

Imports System.IO

Namespace ExempleAdoNetVBNET

Public Class CommandeSQL

Public Shared Sub Main()

Dim strConnexion As String = "Data Source=localhost; Integrated Security=SSPI;" +


"Initial Catalog=Northwind"

Dim strRequete As String = "SELECT CategoryID, CategoryName FROM


Categories;" + "SELECT EmployeeID, LastName FROM Employees"

Try

Dim oConnection As New SqlConnection(strConnexion)

Dim oCommand As New SqlCommand(strRequete, oConnection)

oConnection.Open()

Dim oReader As SqlDataReader = oCommand.ExecuteReader()

Do

43
Console.WriteLine(ControlChars.Tab + "{0}" + ControlChars.Tab + "{1}",
oReader.GetName(0), oReader.GetName(1))

While oReader.Read()

Console.WriteLine(ControlChars.Tab + "{0}" + ControlChars.Tab + "{1}",


oReader.GetInt32(0), oReader.GetString(1))

End While

Loop While oReader.NextResult()

oReader.Close()

oConnection.Close()

Catch e As Exception

Console.WriteLine(("L'erreur suivante a été rencontrée :" + e.Message))

End Try

End Sub 'Main

End Class 'CommandeSQL

End Namespace 'ExempleAdoNetVBNET

6.4 Comment appeler une procédure stockée ?

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.

L'utilisation de la collection Parameters de l'objet Command permet de définir


explicitement les paramètres en entrée et en sortie ainsi que le code retour d'une
procédure stockée.

Tout d'abord informons l'objet Command du type de commande à traiter via la


propriété CommandType ; et donnons lui la valeur StoredProcedure.

44
'Définition du type de commande

oCmd.CommandType = CommandType.StoredProcedure

Définissons ensuite chacun des paramètres en utilisant la méthode Add de la collection


Parameters (dont l'un des prototypes est décrit ci-dessous).

Description

Type

Exemple

Nom du paramètre

String @nom

Type de données

SqlDbType

SqlDbType.Char

Taille de la colonne

Int 25

nom de la colonne source

string nom

' Création d'un paramètre en entrée

Dim oParam As SqlParameter = oCmd.Parameters.Add("@nom", SqlDbType.Char, 25,


Nom)

oParam.Value = "John"

' Création d'un paramètre retour

Dim oParam As SqlParameter = oCmd.Parameters.Add("ReturnValue", SqlDbType.Int)

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.

qu'il faut décrire explicitement le type de paramètres via la propriété


ParameterDirection de l'objet Parameter (sauf pour les paramètres en entrée). Celle-ci
peut prendre l'une des valeurs suivantes : Input, InputOutput, Output ou ReturnValue.

Regardons un exemple implémentant l'exécution de la procédure stockée


"GetCustomerOrders" de la base "Northwind".

45
' Exemple d'appel à une procedure stockée

Imports System

Imports System.Data.SqlClient

Imports System.Data

Imports System.IO

Namespace ExempleAdoNetVBNET

Public Class SQLProcStock

Public Shared Sub Main()

Dim strConnexion As String = "Data Source=localhost; Integrated Security=SSPI;" +


"Initial Catalog=Northwind"

Dim strProcedureStockee As String = "GetCustomerOrders"

Try

Dim oConnection As New SqlConnection(strConnexion)

Dim oCommand As New SqlCommand(strProcedureStockee, oConnection)

oCommand.CommandType = CommandType.StoredProcedure

Dim oParam As SqlParameter =


oCommand.Parameters.Add("@CustomerName", SqlDbType.NVarChar, 50,
"CustomerName")

oParam.Value = "France restauration"

oConnection.Open()

Dim oReader As SqlDataReader = oCommand.ExecuteReader()

Console.WriteLine(ControlChars.Tab + "{0}" + ControlChars.Tab + "{1}" +


ControlChars.Tab + "{2}" + ControlChars.Tab + "{3}", oReader.GetName(0),
oReader.GetName(3), oReader.GetName(4), oReader.GetName(5))

While oReader.Read()

Console.WriteLine(ControlChars.Tab + "{0}" + ControlChars.Tab + "{1}" +


ControlChars.Tab + "{2}" + ControlChars.Tab + "{3}", oReader.GetInt32(0),
oReader.GetString(3), oReader.GetDecimal(4), oReader.GetInt16(5))

End While oReader.Close()

oConnection.Close()

Catch e As Exception

46
Console.WriteLine(("L'erreur suivante a été rencontrée :" + e.Message))

End Try

End Sub 'Main

End Class 'SQLProcStock

End Namespace 'ExempleAdoNetVBNET

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 :

• Créer par programmation un objet DataTable, DataRelation et Constraint dans


un objet DataSet, puis remplir les tables de données.
• Remplir l'objet DataSet de tables de données provenant d'une source de
données relationnelles existante à l'aide d'un objet DataAdapter.
• Charger et rendre persistent le contenu de l'objet DataSet à l'aide de XML.

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.

7.1 L’objet DataAdapter :

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.

7.2 L'objet DataSet:

Le DataSet est une représentation en mémoire des données. On charge le DataSet à


partir de la base de données. Une fois chargé on peut travailler en mode déconnecté.

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.

7.3 L'objet CommandBuilder:

Pour mettre à jour la base après modification du DataSet il faut utiliser l’objet
CommandBuilder.

7.4 Création d'un DataSet

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 :

- une copie exacte de l'objet DataSet existant ;


- un clône de l'objet DataSet qui copie la structure relationnelle ou le schéma,
mais sans aucune des données de l'objet DataSet existant ;
- un sous-ensemble de l'objet DataSet, qui ne contient que les lignes modifiées de
l'objet DataSet existant, à l'aide de la méthode GetChanges.

L'exemple de code suivant montre comment construire une instance d'un objet
DataSet.

Dim customerOrders As DataSet = New DataSet("CustomerOrders")

7.5 Ajout d'un DataTable à un 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.

private void MakeTable()


{
// Create a DataTable.
DataTable table = new DataTable("Product");

// Create a DataColumn and set various properties.


DataColumn column = new DataColumn();
column.DataType = System.Type.GetType("System.Decimal");
column.AllowDBNull = false;
column.Caption = "Price";
column.ColumnName = "Price";
column.DefaultValue = 25;

// Add the column to the table.


table.Columns.Add(column);

// Add 10 rows and set values.


DataRow row;
for(int i = 0; i < 10; i++)
{
row = table.NewRow();
row["Price"] = i + 1;

// Be sure to add the new row to the


// DataRowCollection.
table.Rows.Add(row);
}

7.5.2 }Respect de la casse

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"].

7.5.3 Prise en charge des espaces de noms

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.

7.6 Ajout d'une relation entre différentes tables

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.

Le fait d'ajouter un DataRelation à un objet DataSet ajoute, par défaut, un objet


UniqueConstraint à la table parente et un objet ForeignKeyConstraint à la table enfant.

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.

7.7 Exploration d'une relation entre tables

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.

private void CreateRelation()


{
// Get the DataColumn objects from two DataTable objects
// in a DataSet. Code to get the DataSet not shown here.
DataColumn parentColumn =
DataSet1.Tables["Customers"].Columns["CustID"];
DataColumn childColumn =
DataSet1.Tables["Orders"].Columns["CustID"];
// Create DataRelation.
DataRelation relCustOrder;
relCustOrder = new DataRelation("CustomersOrders",
parentColumn, childColumn);
// Add the relation to the DataSet.
DataSet1.Relations.Add(relCustOrder);
}

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.

// Put the next line into the Declarations section.


private System.Data.DataSet dataSet;

private void MakeDataTables()


{
// Run all of the functions.
MakeParentTable();
MakeChildTable();
MakeDataRelation();
BindToDataGrid();
}

private void MakeParentTable()


{
// Create a new DataTable.
System.Data.DataTable table = new DataTable("ParentTable");
// Declare variables for DataColumn and DataRow objects.
DataColumn column;
DataRow row;

// Create new DataColumn, set DataType,


// ColumnName and add to DataTable.
column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.ColumnName = "id";
column.ReadOnly = true;
column.Unique = true;
// Add the Column to the DataColumnCollection.
table.Columns.Add(column);

// Create second column.


column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "ParentItem";
column.AutoIncrement = false;
column.Caption = "ParentItem";
column.ReadOnly = false;
column.Unique = false;
// Add the column to the table.
table.Columns.Add(column);

// Make the ID column the primary key column.


DataColumn[] PrimaryKeyColumns = new DataColumn[1];
PrimaryKeyColumns[0] = table.Columns["id"];
table.PrimaryKey = PrimaryKeyColumns;

// Instantiate the DataSet variable.


dataSet = new DataSet();

54
// Add the new DataTable to the DataSet.
dataSet.Tables.Add(table);

// Create three new DataRow objects and add


// them to the DataTable
for (int i = 0; i<= 2; i++)
{
row = table.NewRow();
row["id"] = i;
row["ParentItem"] = "ParentItem " + i;
table.Rows.Add(row);
}
}

private void MakeChildTable()


{
// Create a new DataTable.
DataTable table = new DataTable("childTable");
DataColumn column;
DataRow row;

// Create first column and add to the DataTable.


column = new DataColumn();
column.DataType= System.Type.GetType("System.Int32");
column.ColumnName = "ChildID";
column.AutoIncrement = true;
column.Caption = "ID";
column.ReadOnly = true;
column.Unique = true;

// Add the column to the DataColumnCollection.


table.Columns.Add(column);

// Create second column.


column = new DataColumn();
column.DataType= System.Type.GetType("System.String");
column.ColumnName = "ChildItem";
column.AutoIncrement = false;
column.Caption = "ChildItem";
column.ReadOnly = false;
column.Unique = false;
table.Columns.Add(column);

// Create third column.


column = new DataColumn();
column.DataType= System.Type.GetType("System.Int32");
column.ColumnName = "ParentID";
column.AutoIncrement = false;
column.Caption = "ParentID";
column.ReadOnly = false;
column.Unique = false;
table.Columns.Add(column);

55
dataSet.Tables.Add(table);

// Create three sets of DataRow objects,


// five rows each, and add to DataTable.
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 0 ;
table.Rows.Add(row);
}
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i + 5;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 1 ;
table.Rows.Add(row);
}
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i + 10;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 2 ;
table.Rows.Add(row);
}
}

private void MakeDataRelation()


{
// DataRelation requires two DataColumn
// (parent and child) and a name.
DataColumn parentColumn =
dataSet.Tables["ParentTable"].Columns["id"];
DataColumn childColumn =
dataSet.Tables["ChildTable"].Columns["ParentID"];
DataRelation relation = new
DataRelation("parent2Child", parentColumn, childColumn);
dataSet.Tables["ChildTable"].ParentRelations.Add(relation);
}

private void BindToDataGrid()


{
// Instruct the DataGrid to bind to the DataSet, with the
// ParentTable as the topmost DataTable.
dataGrid1.SetDataBinding(dataSet,"ParentTable");
}

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.

private void CreateNewDataRow()


{
// Use the MakeTable function below to create a new table.
DataTable table;
table = MakeNamesTable();

// Once a table has been created, use the


// NewRow to create a DataRow.
DataRow row;
row = table.NewRow();

// Then add the new row to the collection.


row["fName"] = "John";
row["lName"] = "Smith";
table.Rows.Add(row);

foreach(DataColumn column in table.Columns)


Console.WriteLine(column.ColumnName);
dataGrid1.DataSource=table;
}

private DataTable MakeNamesTable()


{
// Create a new DataTable titled 'Names.'
DataTable namesTable = new DataTable("Names");

// Add three column objects to the table.


DataColumn idColumn = new DataColumn();
idColumn.DataType = System.Type.GetType("System.Int32");
idColumn.ColumnName = "id";
idColumn.AutoIncrement = true;
namesTable.Columns.Add(idColumn);

DataColumn fNameColumn = new DataColumn();


fNameColumn.DataType = System.Type.GetType("System.String");
fNameColumn.ColumnName = "Fname";
fNameColumn.DefaultValue = "Fname";
namesTable.Columns.Add(fNameColumn);

57
DataColumn lNameColumn = new DataColumn();
lNameColumn.DataType = System.Type.GetType("System.String");
lNameColumn.ColumnName = "LName";
namesTable.Columns.Add(lNameColumn);

// Create an array for DataColumn objects.


DataColumn [] keys = new DataColumn [1];
keys[0] = idColumn;
namesTable.PrimaryKey = keys;

// Return the new DataTable.


return namesTable;
}

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.

Customer ID: NORTS

Order ID: 10517

Order Date: 4/24/1997 12:00:00 AM

Product: Filo Mix

Quantity: 6

Product: Raclette Courdavault

Quantity: 4

58
Product: Outback Lager

Quantity: 6

Order ID: 11057

Order Date: 4/29/1998 12:00:00 AM

Product: Outback Lager

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

Dim customerOrdersRelation As DataRelation = _

customerOrders.Relations.Add("CustOrders", _

customerOrders.Tables("Customers").Columns("CustomerID"), _

customerOrders.Tables("Orders").Columns("CustomerID"))

Dim orderDetailRelation As DataRelation = _

customerOrders.Relations.Add("OrderDetail", _

customerOrders.Tables("Orders").Columns("OrderID"), _

customerOrders.Tables("OrderDetails").Columns("OrderID"), False)

Dim orderProductRelation As DataRelation = _

customerOrders.Relations.Add("OrderProducts", _

customerOrders.Tables("Products").Columns("ProductID"), _

customerOrders.Tables("OrderDetails").Columns("ProductID"))

59
Dim custRow, orderRow, detailRow As DataRow

For Each custRow In customerOrders.Tables("Customers").Rows

Console.WriteLine("Customer ID:" & custRow("CustomerID").ToString())

For Each orderRow In custRow.GetChildRows(customerOrdersRelation)

Console.WriteLine(" Order ID: " & orderRow("OrderID").ToString())

Console.WriteLine(vbTab & "Order Date: " & _

orderRow("OrderDate").ToString())

For Each detailRow In orderRow.GetChildRows(orderDetailRelation)

Console.WriteLine(vbTab & " Product: " & _

detailRow.GetParentRow(orderProductRelation) _

("ProductName").ToString())

Console.WriteLine(vbTab & " Quantity: " & _

detailRow("Quantity").ToString())

Next

Next

Next

7.8 Utilisation d'un DataSet avec des données existantes

L'objet DataSet est une représentation relationnelle de données résidente en mémoire,


indépendante de toute source de données. Toutefois, le DataSet peut être utilisé avec
les données existantes d'une source de données par l'intermédiaire d'un fournisseur de
données .NET Framework. Un fournisseur de données .NET Framework utilise un
DataAdapter pour remplir le DataSet de données et d'informations de schéma, ainsi

60
que pour répercuter dans la source de données les modifications apportées aux
données.

7.8.1 Remplissage d'un DataSet à partir d'un DataAdapter

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 propriété SelectCommand du DataAdapter est un objet Command qui extrait les


données de la source de données. Les propriétés InsertCommand, UpdateCommand
et DeleteCommand du DataAdapter sont des objets Command qui gèrent les mises à
jour dans la source de données conformément aux modifications faites dans le
DataSet.

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

Dim queryString As String = _ "SELECT CustomerID, CompanyName FROM


dbo.Customers" Dim adapter As SqlDataAdapter = New SqlDataAdapter( _ queryString,
connection) Dim customers As DataSet = New DataSet adapter.Fill(customers,
"Customers")

7.8.1.1 Jeux de résultats multiples

Si le DataAdapter rencontre plusieurs jeux de résultats, il crée plusieurs tables dans le


DataSet. Les tables reçoivent un nom incrémentiel par défaut de TableN, commençant
par « Table » pour Table0. Si un nom de table est passé comme argument à la méthode
Fill, les tables reçoivent un nom incrémentiel par défaut de TableNameN, commençant
par « TableName » pour TableName0.

7.8.1.2 Remplissage d'un DataSet à partir de plusieurs DataAdapters

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

L'exemple de code suivant remplit une liste de clients provenant de la base de


données Northwind sur Microsoft SQL Server 2000 et une liste de commandes provenant
de la base de données Northwind stockée dans Microsoft Access 2000. Les tables
remplies sont reliées par un DataRelation et la liste des clients est ensuite affichée avec
les commandes de ce client.

Dim custAdapter As SqlDataAdapter = New SqlDataAdapter( _ "SELECT * FROM


dbo.Customers", customerConnection) Dim ordAdapter As OleDbDataAdapter = New
OleDbDataAdapter( _ "SELECT * FROM Orders", orderConnection) Dim customerOrders
As DataSet = New DataSet() custAdapter.Fill(customerOrders, "Customers")
ordAdapter.Fill(customerOrders, "Orders") Dim relation As DataRelation = _
customerOrders.Relations.Add("CustOrders", _
customerOrders.Tables("Customers").Columns("CustomerID"), _
customerOrders.Tables("Orders").Columns("CustomerID")) Dim pRow, cRow As DataRow
For Each pRow In customerOrders.Tables("Customers").Rows
Console.WriteLine(pRow("CustomerID").ToString()) For Each cRow In
pRow.GetChildRows(relation) Console.WriteLine(vbTab & cRow("OrderID").ToString())
Next Next

7.8.1.3 Type décimal SQL Server

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.

ADO.NET 2.0 introduit une prise en charge avancée de System.Data.SqlTypes dans le


DataSet.

7.8.1.4 Chapitres OLE DB

Les jeux de lignes hiérarchiques ou chapitres (DBTYPE_HCHAPTER de type OLE DB,


adChapter de type ADO) peuvent être utilisés pour remplir le contenu d'un DataSet.
Lorsque l'objet OleDbDataAdapter rencontre une colonne chapitre pendant une
opération Fill, un DataTable est créé pour cette colonne et cette table est remplie avec
les colonnes et les lignes provenant du chapitre. La table créée pour la colonne
chapitre est nommée à l'aide des noms de la table parente et de la colonne chapitre
sous la forme « ParentTableNameChapteredColumnName ». Si une table
correspondant au nom de la colonne chapitre existe déjà dans le DataSet, la table
actuelle est remplie avec les données du chapitre. S'il n'y a pas de colonne, dans une
table existante, qui corresponde à une colonne trouvée dans le chapitre, une nouvelle
colonne est ajoutée.

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.

Using connection As OleDbConnection = New OleDbConnection( _


"Provider=MSDataShape;Data Provider=SQLOLEDB;" & _ "Data
Source=(local);Integrated " & _ "Security=SSPI;Initial Catalog=northwind") Dim adapter
As OleDbDataAdapter = New OleDbDataAdapter( _ "SHAPE {SELECT CustomerID,
CompanyName FROM Customers} " & _ "APPEND ({SELECT CustomerID, OrderID FROM
Orders} AS Orders " & _ "RELATE CustomerID TO CustomerID)", connection) Dim
customers As DataSet = New DataSet() adapter.Fill(customers, "Customers") End Using

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

CustomerID CompanyName Orders

ALFKI Alfreds Futterkiste 0

ANATR Ana Trujillo Emparedados y helados 1

TableName : CustomersOrders

CustomerID OrderID CustomersOrders

ALFKI 10643 0

65
ALFKI 10692 0

ANATR 10308 1

ANATR 10625 1

7.8.2 Mise à jour de sources de données avec des DataAdapters

La méthode Update de l'objet DataAdapter est appelée pour répercuter les


modifications provenant d'un objet DataSet dans la source de données. La méthode
Update, comme la méthode Fill, prend comme arguments une instance d'un DataSet
et un objet DataTable optionnel ou un nom DataTable. L'instance DataSet est le
DataSet qui contient les modifications apportées et le DataTable identifie la table d'où
les modifications doivent être extraites.

Lorsque vous appelez la méthode Update, le DataAdapter analyse les modifications


apportées et exécute la commande appropriée (INSERT, UPDATE ou DELETE). Lorsque le
DataAdapter trouve une modification dans un objet DataRow, il utilise InsertCommand,
UpdateCommand ou DeleteCommand pour traiter la modification. Cela vous permet
d'optimiser la performance de votre application ADO.NET en spécifiant la syntaxe de
commande au moment du design et, si possible, par l'intermédiaire de l'utilisation des
procédures stockées. Vous devez explicitement définir les commandes avant d'appeler
Update. Si Update est appelé et si la commande appropriée n'existe pas pour une mise
à jour particulière (par exemple, pas de DeleteCommand pour les lignes supprimées),
une exception est levée.

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.

Dim adapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT CategoryID, CategoryName FROM Categories", connection)

67
adapter.UpdateCommand = New SqlCommand( _

"UPDATE Categories SET CategoryName = @CategoryName " & _

"WHERE CategoryID = @CategoryID", connection)

adapter.UpdateCommand.Parameters.Add( _

"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim parameter As SqlParameter = adapter.UpdateCommand.Parameters.Add( _

"@CategoryID", SqlDbType.Int)

parameter.SourceColumn = "CategoryID"

parameter.SourceVersion = DataRowVersion.Original

Dim dataSet As DataSet = New DataSet

adapter.Fill(dataSet, "Categories")

Dim row As DataRow = dataSet.Tables("Categories").Rows(0)

row("CategoryName") = "New Category"

adapter.Update(dataSet, "Categories")

7.8.2.1 Colonnes AutoIncrement

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

Dans de nombreuses circonstances, l'ordre dans lequel les modifications apportées


dans le DataSet sont transmises à la source de données est important. Par exemple, si
une valeur de clé primaire d'une ligne existante est mise à jour et qu'une nouvelle ligne
est ajoutée avec la nouvelle valeur de clé primaire, il est important de traiter la mise à
jour avant l'insertion.

Vous pouvez utiliser la méthode Select du DataTable pour retourner un tableau


DataRow qui fait uniquement référence à des lignes avec un RowState particulier. Vous
pouvez alors passer le tableau DataRow retourné à la méthode Update du
DataAdapter pour traiter les lignes modifiées. En spécifiant un sous-ensemble des lignes
à mettre à jour, vous pouvez contrôler l'ordre dans lequel les insertions, mises à jour et
suppressions sont traitées.

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.

Dim table As DataTable = dataSet.Tables("Customers")

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))

7.8.3 Ajout de contraintes existantes à un DataSet

La méthode Fill du DataAdapter remplit un objet DataSet uniquement avec les


colonnes et les lignes de la table d'une source de données ; bien que les contraintes
soient généralement définies par la source de données, la méthode Fill n'ajoute pas
ces informations de schéma au DataSet par défaut. Pour remplir un DataSet avec les
informations de contrainte de clé primaire existantes provenant d'une source de
données, vous pouvez appeler la méthode FillSchema du DataAdapter ou affecter
AddWithKey à la propriété MissingSchemaAction du DataAdapter avant d'appeler Fill.
Cela garantira que les contraintes de clé primaire dans le DataSet reflètent celles de la
source de données. Les informations de contrainte de clé étrangère ne sont pas
incluses et doivent être explicitement créées.

L'ajout d'informations de schéma à un DataSet avant de le remplir avec les données


garantit que les contraintes de clé primaire sont incluses avec les objets DataTable du
DataSet. En conséquence, lorsque des appels supplémentaires sont établis pour remplir
le DataSet, les informations de colonne de clé primaire sont utilisées pour faire
correspondre les nouvelles lignes de la source de données avec les lignes actuelles de
chaque DataTable ; les données actuelles des tables sont remplacées par celles de la
source de données. Sans les informations de schéma, les nouvelles lignes de la source
de données sont ajoutées au DataSet, ce qui a pour résultat des lignes doubles.

L'utilisation de FillSchema ou l'affectation de AddWithKey à MissingSchemaAction


exigent un traitement supplémentaire au niveau de la source de données afin de
déterminer les informations de colonne de clé primaire. Ce traitement supplémentaire
peut gêner la performance. Si vous connaissez les informations de clé primaire au

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.

Dim custDataSet As DataSet = New DataSet() custAdapter.FillSchema(custDataSet,


SchemaType.Source, "Customers") custAdapter.Fill(custDataSet, "Customers")

L'exemple de code suivant montre comment ajouter des informations de schéma à un


DataSet à l'aide de la propriété MissingSchemaAction.AddWithKey de la méthode Fill.

Dim custDataSet As DataSet = New DataSet()

custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
custAdapter.Fill(custDataSet, "Customers")

Jeux de résultats multiples

Si le DataAdapter trouve plusieurs jeux de résultats retournés à partir de


SelectCommand, il créera plusieurs tables dans le DataSet. Les tables recevront un nom
incrémentiel par défaut de base zéro de TableN, en commençant par Table au lieu de
« Table0 ». Si un nom de table est passé comme argument à la méthode FillSchema, les
tables recevront un nom incrémentiel de base zéro de TableNameN, en commençant
par TableName au lieu de « TableName0 ».

7.8.4 Utilisation des paramètres avec un DataAdapter

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.

UPDATE Customers SET CompanyName = @CompanyName WHERE CustomerID =


@CustomerID

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.

adapter.Parameters.Add( _ "@CompanyName", SqlDbType.NChar, 15,


"CompanyName") Dim parameter As SqlParameter = _
adapter.UpdateCommand.Parameters.Add("@CustomerID", _ SqlDbType.NChar, 5,
"CustomerID") parameter.SourceVersion = DataRowVersion.Original

La méthode Add de la collection Parameters prend le nom du paramètre, le type


spécifique au DataAdapter, la taille (si elle est applicable au type) et le nom du
SourceColumn du DataTable. Notez que le SourceVersion du paramètre @CustomerID

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.

7.8.4.1 Exemple SqlClient

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

Vous aimerez peut-être aussi