Plan Accès aux données dans le framework.net Lionel Seinturier 1. Mode connecté Mode non connecté 2. 3. Université Lille 1 Lionel.Seinturier@univ-lille1.fr 27/3/14 1 Lionel Seinturier 2 Lionel Seinturier 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 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 Principe présentation traitement donnée le programme ouvre une connexion il envoie des requêtes SQL il récupère les résultats... il ferme la connexion client serveur d'applications SGBD 3 Lionel Seinturier 4 Lionel Seinturier
Historique VB RDO ODBC évolutions unification DAO C++ Fournisseur (provider) et connection string Implémentation de l'api pour un type de sources de données Un provider par type de base de données (SQL Server, MySQL, etc.) Connection string : identifie la source de données à laquelle on se connecte OLE-DB ADO ts langages API COM simplification (+ haut niveau) chaîne de caractères liste de couples propriété=valeur format dépend du provider Exemple : "Server=...;Database=...;" framework.net 5 Lionel Seinturier 6 Lionel Seinturier Utilisation Utilisation de (suite) L'API est définie dans System.Data 1. Ouverture d'une connexion avec la base test SqlConnection cx = new SqlConnection("Server=localhost;Database=test;"); cx.open(); 2. Envoi d'une requête SELECT SqlCommand cmd = new SqlCommand("SELECT * FROM ages",cx); SqlDataReader reader = cmd.executereader(); Envoi d'une requête CREATE, INSERT ou UPDATE SqlCommand cmd = new SqlCommand("INSERT INTO ages VALUES ('toto',12)",cx); 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.SqlClient; public class TestADONet { public static void Main( String[] args ) { SqlConnection cx = new SqlConnection("Server=localhost;Database=test;"); cx.open(); SqlCommand cmd = new SqlCommand("SELECT * FROM ages",cx); SqlDataReader reader = cmd.executereader(); 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? SqlCommand cmd = new SqlCommand ("SELECT * FROM ages WHERE nom=? AND age>?",cx); 2. Valeurs des paramètres ajoutés à la commande cmd.parameters.add(new Parameter("Bob"),cx); cmd.parameters.add(new Parameter(Convert.ToInt32(5)),cx); new Parameter( 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 SqlDataReader reader = cmd.executereader();... 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 SqlCommand cmd = new SqlCommand("GetRange",cx); cmd.commandtype = CommandType.StoredProcedure; cmd.parameters.add( new Parameter("age",Convert.ToInt32(5) ); SqlDataReader reader = cmd.executereader();... 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) SqlCommand cmd1 = new SqlCommand("INSERT INTO ages VALUES ('Pierre',12)",cx); SqlCommand cmd2 = new SqlCommand("UPDATE ages SET age=15 WHERE nom='joe'",cx); SqlTransaction trans = cx.begintransaction(); cmd1.transaction = trans; cmd2.transaction = trans; cmd1.executenonquery(); cmd2.executenonquery(); trans.commit(); déclaration du début de la transaction validation de la transaction 13 Lionel Seinturier Exemple CREATE TABLE comptes (nom VARCHAR(30) PRIMARY KEY, solde FLOAT CHECK(solde>=0) ); SqlCommand cmd1, cmd2; cmd1 = new SqlCommand("UPDATE comptes SET solde=solde+montant WHERE nom='paul'",cx); cmd2 = new SqlCommand("UPDATE comptes SET solde=solde-montant WHERE nom='bob'",cx); SqlTransaction 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() SqlConnection cx = new SqlConnection("Server=localhost;Database=pubs;"); cx.open(); SqlDataAdapter adapter = new SqlDataAdapter(); adapter.selectcommand = new SqlCommand("SELECT * FROM comptes",cx); DbCommandBuilder builder = new SqlCommandBuilder(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 nom LIKE '*ob*' - fonctions sum, avg, count, min, max 19 Lionel Seinturier 20 Lionel Seinturier
Comparaison - JDBC JDBC c/s oui oui accès provider driver désignation Server= 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 21 Lionel Seinturier Comparaison - JDBC JDBC méta-données oui oui batch non oui pool de cx oui (provider) oui (JNDI) 22 Lionel Seinturier Plan 1. Mode connecté Mode non connecté 2. 3. (EF) Mapping objet relationnel mise en correspondance d'objets (C#, VB, etc.) et SGBD Avantages (par rapport à ) typage manipulation d'objets métier (plutôt que SQL) Stockage des données une table par classe données utilisables indifféremment EF,, "directement" en SQL 23 Lionel Seinturier 24 Lionel Seinturier
Principe de base Relations entre tables class Blog { public int BlogId { get; set; public string Name { get; set; Notion de contexte pour faire le lien entre objets et SGBD using System.Data.Entity; class BlogContext : DbContext { DbSet<Blog> Blogs { get; set; Utilisation var db = new BlockContext(); var blog = new Blog { BlogId=1; Name=".NET Blog"; ; db.blogs.add( blog ); db.savechanges(); Stockage base : <nom projet VSudio>.BlogContext table : Blogs 25 Lionel Seinturier class Blog { public int BlogId { get; set; public string Name { get; set; public virtual List<Post> Posts { get; set; class Post { public int PostId { get; set; public string Title { get; set; public string Content { get; set; public virtual Blog Blog { get; set; relations 1-1, 1-n ou n-m virtual = chargement des données uniquement si accédées 26 Lionel Seinturier Annotations Recherches Précisent la mise en correspondance objet relationnel redéfinissent les noms par défaut pour les tables, colonnes renseignent les informations liées aux clés autres : renseignent types SQL, valeurs auto-générées, index [Table("Journal")] class Blog { [Key] public int BlogId { get; set; [Column="TitreDuJournal"] public string Name { get; set; public virtual List<Post> Posts { get; set; à partir de clé primaire à partir d'une fonction de filtrage var blog = db.blogs.find(3); // null si la clé 3 n'existe pas var blogs = db.blogs.where( b => b.name=="bob" ); // résultat de type collection class Post { [Key] public int PostId { get; set; public string Title { get; set; public string Content { get; set; public virtual Blog Blog { get; set; [ForeignKey("BlogId"] public int BlogId {get; set; 27 Lionel Seinturier 28 Lionel Seinturier
Modifications ajouts, modifications, suppressions SaveChange() pour prendre en compte modifications (une ou plusieurs) var blog = new Blog { BlogId=1; Name=".NET Blog"; ; db.blogs.add( blog ); db.savechanges(); var blog = db.blogs.find(3); blog.name = "John Doe"; db.savechanges(); Plan 1. Mode connecté Mode non connecté 2. 3. var blog = db.blogs.find(3); db.blogs.remove( blog ); db.savechanges(); 29 Lionel Seinturier 30 Lionel Seinturier Langage de requêtage SQL intégré au langage de programmation (C#, VB, ) Nouveaux mots-clés from : variable d'itération in : collection sur laquelle s'effectue l'itération where : condition de sélection de l'élément courant select : valeur à sélectionner int[] tab = new int[]{ 6, 3, 1, 2, 5, 4, 6 ; IEnumerable<int> pairs = from number in tab where number % 2 == 0 select number; foreach(int i in pairs) {... Collections var people = new List<User>() { new User { Name = "Bob", Age = 24, Tel = "06", new User { Name = "Anne", Age = 26, Tel = "07" ; var persons = where p.age > 25 && p.name.startswith("a") select p; Type des données retournées (classe anonyme) var persons = select new { p.nom, p.tel ; 31 Lionel Seinturier 32 Lionel Seinturier
Jointures var addresses = new List<Address>() { new User { Name = "Bob", Ville = "Lille", new User { Name = "Pat", Ville = "Paris" ; var knownadresses = from a in addresses where p.name == a.name select p.name; Tri et regroupement var persons = orderby p.name, p.age select p; var knownadresses = from a in addresses where p.name == a.name group p by p.ville select p.name; 33 Lionel Seinturier 34 Lionel Seinturier Opérateurs OfType<T> Min, Max, Sum, Average object[] values = { 1, "Tom", 'T', 12.5, 3, true, 20 ; var results = values.oftype<int>(); foreach(int i in results) { Console.WriteLine(i); int[] IntegerValues = { 0, 2, 5, 6, 7 ; int max = IntegerValues.Max(); int min = IntegerValues.Min(); int sum = IntegerValues.Sum(); double average = IntegerValues.Average(); Opérateurs Where(λ) Count(λ) ToArray, ToList var values = new object[] { 1, "Tom", 'T', 12.5, 3, true, 20 ; var val = values.where( v => v.tostring().length >= 3 ); int i = values.count( v => v.tostring().length >= 3 ); object[] array = values.toarray(); List<object> list = array.tolist(); 35 Lionel Seinturier 36 Lionel Seinturier