Lionel Seinturier INRIA Futurs Lille Projet JACQUARD Université Pierre & Marie Curie Lionel.Seinturier@lifl.fr 30/8/04 ActiveX Data Object.Net () API d accès (local ou distant) à une source de données : SGBD, tableur, fichier, messagerie, Fonctionne selon un principe client/serveur (local ou distant) client = le programme (C#, VB, C++, ) serveur = la source de données Principe le programme ouvre une connexion il envoie des requêtes SQL il récupère les résultats... il ferme la connexion 1 Lionel Seinturier 2 Lionel Seinturier ActiveX Data Object.Net () API d'interaction avec un SGBD nombreuses utilisations - sauvegarde de données de manière sûre - exploration du contenu d'un SGBD - client/serveur 3 tiers Historique VB RDO ODBC Accès SGBD évolutions DAO C++ unification OLE-DB ts langages API COM présentation traitement donnée simplification (+ haut niveau) ADO framework.net client serveur d'applications SGBD 3 Lionel Seinturier 4 Lionel Seinturier
Fournisseur (provider) Implémentation de l API pour un type de sources de données Principaux providers SQL Server (>= 7.0) System.Data.SqlClient.SqlConnection OLE-DB (SQL Server < 7.0, ) System.Data.OleDb.OleDbConnection ODBC (Excel, Access, ) System.Data.Odbc.OdbcConnection MySQL http://sourceforge.net/projects/mysqlnet/ Fournisseur Chaque provider = une classe OLE-DB ODBC Connection string Identifie la source de données à laquelle on se connecte chaîne de caractères liste de couples propriété=valeur format dépend du provider Exemple ODBC "Driver={SQL Server;Server=localhost;Database=mydb;Uid=login;Pwd=passwd" OLE-DB Provider=sqloledb;Data Source= ;Database= ;User Id= ;Password= voir http://geekswithblogs.net/brcraju/archive/2004/02/03/1689.aspx 5 Lionel Seinturier 6 Lionel Seinturier Utilisation de Utilisation de (suite) L'ensemble de l'api est définie dans System.Data 1. Ouverture d'une connexion avec la base comptes OdbcConnection cx = new OdbcConnection("Driver={SQL Server;Server=localhost;Database=pubs;"); cx.open(); 2. Envoi d'une requête SELECT OdbcCommand cmd = new OdbcCommand("SELECT * FROM ages"); Envoi d'une requête CREATE, INSERT ou UPDATE OdbcCommand cmd = new OdbcCommand("INSERT INTO ages VALUES ('toto',12)"); cmd.executenonquery(); 3. Récupération du résultat reader.read() retourne vrai tant qu'il reste des enregistrements dans le résultat et positionne le curseur sur l'enregistrement suivant reader.getstring(int column) (ex. : reader.getstring(0) ) retourne la valeur de la colonne 0 de type String de l enregistrement courant GetInt32, GetBoolean, GetByte, GetDouble, GetFloat idem pour des colonnes de type int, boolean, byte, double ou float while( reader.read() ) { String nom = reader.getstring(0); int age = reader.getint32(1); Console.WriteLine( nom + " a " + age + " ans" ); 7 Lionel Seinturier 8 Lionel Seinturier
Utilisation de (code complet) Types de requêtes SQL using System.Data; using System.Data.Odbc; public class TestADONet { public static void Main( String[] args ) { OdbcConnection cx = new OdbcConnection( "Driver={SQL Server;Server=localhost;Database=pubs;" ); cx.open(); OdbcCommand cmd = new OdbcCommand("SELECT * FROM ages",cx); while (reader.read()) { string nom = reader.getstring(0); int age = reader.getint32(1); Console.WriteLine( nom + " a " + age + " ans" ); cx.close(); "normale" - interprétée à chaque exécution précompilée - paramétrable - préparée pour être exécutée plusieurs fois - gérée par le programme procédure stockée - paramétrable - écrite dans le langage interne du SGBD (ex SQL Server Transac-SQL) - gérée par le SGBD + masque schéma base - langage propriétaire (- évolution) + meilleures perf - risque de mélange + validées par rapport schéma base logiques traitement/donnée 9 Lionel Seinturier 10 Lionel Seinturier Requêtes SQL précompilées Procédures stockées 1. Possibilité de définition de 1 ou +sieurs paramètres! caractères? OdbcCommand cmd = new OdbcCommand ("SELECT * FROM ages WHERE nom=? AND age>?",cx); 2. Valeurs des paramètres ajoutés à la commande cmd.parameters.add(new OdbcParameter("","Bob"),cx); cmd.parameters.add(new OdbcParameter("",Convert.ToInt32(5)),cx); new OdbcParameter( string name, object value ) paramètres ajoutés dans l'ordre de leur définition dans la requête name non significatif dans ce contexte (voir procédure stockée) 3. Exécution de la requête... Exemple de procédure stockée Transact-SQL (SQL Server) CREATE PROCEDURE [pubs].[getrange] @age int AS SELECT nom FROM ages WHERE age < @age GO Le code d appel de la procédure OdbcCommand cmd = new OdbcCommand("GetRange",cx); cmd.commandtype = CommandType.StoredProcedure; cmd.parameters.add( new OdbcParameter("age",Convert.ToInt32(5) );... 11 Lionel Seinturier 12 Lionel Seinturier
Transactions Transactions Groupes de requêtes devant être exécutés de façon indivisible La transaction doit être - validée (commit)! les résultats ne sont visibles qu'à partir de ce moment -ou annulée(rollback) OdbcCommand cmd1 = new OdbcCommand("INSERT INTO ages VALUES ('Pierre',12)",cx); OdbcCommand cmd2 = new OdbcCommand("UPDATE ages SET age=15 WHERE nom='joe'",cx); OdbcTransaction trans = cx.begintransaction(); cmd1.transaction = trans; cmd2.transaction = trans; cmd1.executenonquery(); déclaration du début de la transaction cmd2.executenonquery(); trans.commit(); validation de la transaction 13 Lionel Seinturier Exemple CREATE TABLE comptes (nom VARCHAR(30) PRIMARY KEY, solde FLOAT CHECK(solde>=0) ); OdbcCommand cmd1, cmd2; cmd1 = new OdbcCommand("UPDATE comptes SET solde=solde+montant WHERE nom='paul'",cx); cmd2 = new OdbcCommand("UPDATE comptes SET solde=solde-montant WHERE nom='bob'",cx); OdbcTransaction trans = cx.begintransaction(); try { cmd1.transaction = trans; cmd2.transaction = trans; cmd1.executenonquery(); cmd2.executenonquery(); trans.commit(); catch( Exception e ) { trans.rollback(); 14 Lionel Seinturier Accès aux données en mode déconnecté Accès aux données en mode déconnecté par défaut c/s connecté vers SGBD + 1 seule copie des données (SGBD) + mises à jour simples connecté vs non connecté n messages petite taille vs 1 message grande taille rés eau rés eau SELECT SELECT datasets : représentation mémoire des données d'un SGBD DataSet adapter.update(dataset) Data Adapter adapter.fill(dataset) SGBD Déconnecté pouvoir consulter/modifier les données off line économiser les ressources réseaux (connexions moins longues) travailler sur des données en mémoire plutôt que directement sur un SGBD 15 Lionel Seinturier DataAdapter : gère liaison mémoire (DataSet) SGBD! contient les requêtes SQL (select, update, insert) associées aux données 16 Lionel Seinturier
DataSet Exemple d'utilisation d'un DataSet Un DataSet contient des DataTable données sous forme de table - des DataColumn - nom, type, propriétés (autoincrement, unique, readonly, maxlength, ), -des DataRow -valeurs - des DataConstraint des DataRelation relation entre 2 DataTable une DefaultView Un DataSet peut être consulté modifié (valeurs, lignes) sauvegardé/chargé en XML! mise à jour BD lors de Update() OdbcConnection cx = new OdbcConnection( "Driver={SQL Server;Server=localhost;Database=pubs;" ); cx.open(); OdbcDataAdapter adapter = new OdbcDataAdapter(); adapter.selectcommand = new OdbcCommand("SELECT * FROM comptes",cx); OdbcCommandBuilder builder = new OdbcCommandBuilder(adapter); DataSet dataset = new DataSet(); adapter.fill(dataset); cx.close(); for( int i=0 ; i<dataset.tables.count ; i++ ) { DataTable table = dataset.tables[i]; DataColumnCollection columns = table.columns; for( int j=0 ; j<table.rows.count ; j++ ) { DataRow row = table.rows[j]; Console.WriteLine("Row "+j+": "+row["nom"]+" "+row["solde"]); 17 Lionel Seinturier 18 Lionel Seinturier Mise à jour d'un DataSet DataView Modification d'une valeur table.rows[0][0] = "Bill"; adapter.update(dataset); Ajout d'une ligne DataRow mydatarow = table.newrow(); mydatarow["nom"] = "John"; mydatarow["solde"] = 123; table.rows.add(mydatarow); adapter.update(dataset); Vue (pas de copie des données) sur une Datatable séléction tri Mise à jour données dans la vue = maj des données dans la DataTable DataView view = new DataView(table); view.rowfilter = "nom='bob'"; // sélection de(s) Bob for( int i=0 ; i < view.count ; i++ ) { view.delete(i); // suppression aussi dans la DataTable view.sort = "nom, age DESC"; // d'abord pas nom puis par age décroissant Expression de sélection "à la SQL" -opérateur LIKE - fonctions sum, avg, count, min, max nom LIKE '*ob*' 19 Lionel Seinturier 20 Lionel Seinturier
Utilisation des DataSet avec les DataGrid Trou de sécurité (bien connu) DataGrid Attaque "SQL injection" à partir de formulaires Web composants WinForm et WebForm d'édition de données tabulaires datagrid.datasource = dataset.tables[0]; datagrid.databind(); CREATE TABLE notes ( netudiant text, note float ); Formulaire traité ASP (idem PHP, JSP, ) String num =... String requete = "SELECT * FROM notes WHERE netudiant='"+num+"'"; OdbcCommand cmd = new OdbcCommand(requete,cx); Saisie '; SELECT * FROM notes WHERE ''=' DataGrid DataSet Data Adapter SGBD requete = SELECT * FROM notes WHERE netudiant=''; SELECT * FROM notes WHERE ''=''! toutes les notes!! 21 Lionel Seinturier 22 Lionel Seinturier Comparaison - JDBC JDBC c/s oui oui accès provider driver désignation Driver= jdbc:mysql:// initialisation new Class.forName(" ") connexion Open() DriverManager.getConnection commande Command Statement/PreparedStatement/CallableState curseur résultat DataReader ResultSet curs. multi-dir non oui curs. maj non oui deconnecté DataSet RowSet (JDBC 3.0) transaction cx.begintransaction() cx.setautocommit(false) trans.commit() cx.commit() trans.rollback() cx.rollback() niv. isolation oui oui 23 Lionel Seinturier Comparaison - JDBC JDBC niv. isolation oui oui méta-données oui oui batch non oui pool de cx oui (provider) oui (JNDI) 24 Lionel Seinturier