1 Lionel Seinturier
Accès aux données dans le framework .NET ADO.NET – Entity Framework – LINQ
Lionel Seinturier Université Lille 1
Lionel.Seinturier@univ-lille1.fr
27/3/14
2 Lionel Seinturier
1. ADO.NET Mode connecté Mode non connecté 2. Entity Framework 3. LINQ
ADO.NET
ActiveX Data Object .NET (ADO.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
ADO.NET
ActiveX Data Object .NET (ADO.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
serveur d'applications
client SGBD
donnée traitement
présentation
ADO.NET
ADO.NET 5 Lionel Seinturier
Historique
ODBC
RDO DAO
OLE-DB
ADO
ADO.NET
C++
VB
ts langages – API COM évolutions
unification
simplification (+ haut niveau)
framework .NET
ADO.NET 6 Lionel Seinturier
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
• chaîne de caractères
• liste de couples propriété=valeur
• format dépend du provider
Exemple : "Server=...;Database=...;"
ADO.NET
Utilisation
L'API ADO.NET 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();
ADO.NET
Utilisation de ADO.NET (suite)
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" );
}
ADO.NET 9 Lionel Seinturier 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();
} }
Utilisation de ADO.NET (code complet)
ADO.NET 10 Lionel Seinturier
Types de requêtes SQL
• "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
ADO.NET
Requêtes SQL précompilé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();
...
ADO.NET
Procédures stockées
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();
...
ADO.NET 13 Lionel Seinturier
déclaration du début de la transaction validation de la transaction
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();
ADO.NET 14 Lionel Seinturier
Transactions
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();
}
ADO.NET
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
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 rés
eau
SELECT …
rés eau
SELECT …
ADO.NET
Accès aux données en mode déconnecté
• datasets : représentation mémoire des données d'un SGBD
SGBD Data
Adapter DataSet
adapter.Fill(dataset) adapter.Update(dataset)
DataAdapter : gère liaison mémoire (DataSet) – SGBD
! contient les requêtes SQL (select, update, insert) associées aux données
ADO.NET 17 Lionel Seinturier
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) ! mise à jour BD lors de Update()
• sauvegardé/chargé en XML
ADO.NET 18 Lionel Seinturier
Exemple d'utilisation d'un DataSet
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"]);
} }
ADO.NET
Mise à jour d'un DataSet
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);
ADO.NET
DataView
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
ADO.NET 21 Lionel Seinturier
Comparaison ADO.NET - JDBC
ADO.NET 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
ADO.NET 22 Lionel Seinturier
Comparaison ADO.NET - JDBC
ADO.NET JDBC
méta-données oui oui
batch non oui
pool de cx oui (provider) oui (JNDI)
Plan
1. ADO.NET Mode connecté Mode non connecté 2. Entity Framework 3. LINQ
Entity Framework
Entity Framework (EF)
Mapping objet relationnel
• mise en correspondance d'objets (C#, VB, etc.) et SGBD Avantages (par rapport à ADO.NET)
• typage
• manipulation d'objets métier (plutôt que SQL) Stockage des données
• une table par classe
• données utilisables indifféremment EF, ADO.NET, "directement" en SQL
Entity Framework 25 Lionel Seinturier
Entity Framework
Principe de base
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
Entity Framework 26 Lionel Seinturier
Entity Framework
Relations entre tables
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
Entity Framework
Annotations
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; }}
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; }}
Entity Framework
Recherches
• à 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
Entity Framework 29 Lionel Seinturier
Entity Framework
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();
var blog = db.Blogs.find(3);
db.Blogs.Remove( blog );
db.SaveChanges();
30 Lionel Seinturier
Plan
1. ADO.NET Mode connecté Mode non connecté 2. Entity Framework 3. LINQ
LINQ
LINQ (Language INTegrated Query)
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) { ... }
LINQ
LINQ (Language INTegrated Query)
Collections
var people = new List<User>() {
new User { Name = "Bob", Age = 24, Tel = "06" }, new User { Name = "Anne", Age = 26, Tel = "07" } };
var persons = from p in people
where p.Age > 25 && p.Name.StartsWith("A") select p;
Type des données retournées (classe anonyme)
var persons = from p in people
select new { p.Nom, p.Tel };
LINQ 33 Lionel Seinturier
LINQ
LINQ (Language INTegrated Query)
Jointures
var addresses = new List<Address>() {
new User { Name = "Bob", Ville = "Lille" }, new User { Name = "Pat", Ville = "Paris" } };
var knownAdresses = from p in people from a in addresses where p.Name == a.Name select p.Name;
LINQ 34 Lionel Seinturier
LINQ
LINQ (Language INTegrated Query)
Tri et regroupement
var persons = from p in people orderby p.Name, p.Age select p;
var knownAdresses = from p in people from a in addresses where p.Name == a.Name group p by p.Ville select p.Name;
LINQ
LINQ (Language INTegrated Query)
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();
LINQ
LINQ (Language INTegrated Query)
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();