Data Access - ROMAGNY13romagny13.com/wp-content/uploads/2015/04/Data-Overview.pdf · Requêtes...
Transcript of Data Access - ROMAGNY13romagny13.com/wp-content/uploads/2015/04/Data-Overview.pdf · Requêtes...
DATA ACCESS Tour d’horizon
Jérôme Romagny
Résumé Ce document est évolutif et sera mis à jour régulièrement
1
1
I. ENTERPRISE LIBRARY DATA ACCESS APPLICATION BLOCK ........................................................................................ 2 1. Fichier de configuration ......................................................................................................................... 2 2. DatabaseProviderFactory et Database .................................................................................................. 4 3. Unity ...................................................................................................................................................... 4 4. Commande de type « Text » .................................................................................................................. 5 5. Commande de type « StoredProcedure » .............................................................................................. 6 6. MapBuider<T> ....................................................................................................................................... 6 7. DataSet .................................................................................................................................................. 6 8. Transactions .......................................................................................................................................... 6 9. Async ...................................................................................................................................................... 7
II. ADO.NET ...................................................................................................................................................... 9 1. Configuration ......................................................................................................................................... 9 2. Connexion .............................................................................................................................................. 9 3. Commande........................................................................................................................................... 10
III. SQL ........................................................................................................................................................... 14 1. Création de tables ................................................................................................................................ 14 2. CRUD Insérer, modifier, supprimer des données ................................................................................. 15 3. Requêtes Select .................................................................................................................................... 15
IV. SQL SERVER ................................................................................................................................................ 19 1. Sauvegarde / Restauration de base de données ................................................................................. 19 2. Sécurité ................................................................................................................................................ 21
2
2
I. Enterprise Library Data Access Application Block
http://msdn.microsoft.com/en-us/library/ff648951.aspx
1. Fichier de configuration
Ouvrir le fichier de configuration de l’application avec « Enterprise Library Configuration »
Ajout du bloc « Data »
Renseigner la chaine de connexion
3
3
.. le provider et le nom de la chaine de connexion
Puis sauvegarder les changements
Exemple de fichier de configuration mis à jour
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=6.0.0.0, Culture=neutral" requirePermission="false" /> </configSections> <dataConfiguration defaultDatabase="DefaultConnection" /> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=dbPeople;Integrated Security=SSPI;" providerName="System.Data.SqlClient" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" /> </startup> </configuration>
Attention : Il y a un bug, retirer PublicKeytoken=null
Astuce : Mettre à jour les références des QuickStarts/Hands-on depuis le gestionnaire de package
NuGet
4
4
2. DatabaseProviderFactory et Database private DatabaseProviderFactory _factory; private Database _db; public PeopleRepository() { _factory = new DatabaseProviderFactory(); _db = _factory.CreateDefault(); }
Si on a plusieurs chaines de connexions définies et que l’on désire utiliser une chaine qui n’est pas
définie par défaut
_db = _factory.Create("MyConnectionString");
3. Unity
« Register » Enregistrement d’un Service
_container.RegisterType<IPeopleRepository, PeopleRepository>();
…Utilisation du service (pas de constructeur par défaut) : injection de dépendance.
public class ShellViewModel : BindableBase { private IPeopleRepository _peopleRepository; public ShellViewModel(IPeopleRepository peopleRepository) { _peopleRepository = peopleRepository; } } « Resolve »
var shell =_container.Resolve<Shell>(); shell.Show(); Supprimer « StartupUri="MainWindow.xaml"» de App.xaml
Exemple Wpf, enregistrement d’un service et lancement du Shell public partial class App : Application { private IUnityContainer _container; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); _container = new UnityContainer(); // register a service _container.RegisterType<IPeopleRepository, PeopleRepository>(); var shell =_container.Resolve<Shell>(); shell.Show(); } }
5
5
Le Shell
public partial class Shell : Window { public MainWindow(ShellViewModel viewModel) { InitializeComponent(); DataContext = viewModel; } }
4. Commande de type « Text »
requête SQL sans paramètre. Utiliser la signature « directe »
ExecuteReader var people = new List<Person>(); using (var reader = _db.ExecuteReader(CommandType.Text, "select * from dbo.Person")) { while (reader.Read()) { var person = new Person(); person.Id = reader.GetInt32(reader.GetOrdinal("PersonID")); person.FirstName = reader.GetString(reader.GetOrdinal("FirstName")); person.LastName = reader.GetString(reader.GetOrdinal("LastName")); person.Email = reader.GetString(reader.GetOrdinal("Email")); people.Add(person); } } ExecuteScalar var result = (int)_db.ExecuteScalar(CommandType.Text, "Select count(*) from dbo.Person"); ExecuteNonQuery var result = _db.ExecuteNonQuery(CommandType.Text, "update dbo.Person set CategoryID=1 where LastName='Bellin' ");
Requête SQL avec paramètre : Créer une commande puis la passer afin qu’elle soit exécutée
Créer une commande et ajout de paramètre nommé
var command = _db.GetSqlStringCommand("Select * from dbo.Person where PersonID=@id"); _db.AddInParameter(command, "id", DbType.Int32, id); …Exécution
using (var reader = _db.ExecuteReader(command) { // etc. }
+ AddParameter,AddInParameter,AddOutParameter,EgtParameterValue et SetParameterValue
6
6
ExecuteSqlStringAccessor<T> var people = _db.ExecuteSqlStringAccessor<Person>("select * from dbo.Person");
5. Commande de type « StoredProcedure »
Utiliser la signature directe en passant les paramètres si besoin en tableau d’objets
using (var reader = _db.ExecuteReader("sp_getperson", new object[] { id })) { // etc. }
Il est possible de créer une commande pour procédure stockée var commandWithStoredProedure = _db.GetStoredProcCommand("sp_getperson", new object[] { id });
ExecuteSprocAccessor<T> var people = _db.ExecuteSprocAccessor<Person>("sp_getpeople");
6. MapBuider<T>
Mappage « WithFunc » (avec fonction)
Exemple on ajoute une propriété FullName à la classe Personne qui va être construite avec le
MapBuilder.
var mapper = MapBuilder<Person>.MapAllProperties().Map(p => p.FullName).WithFunc((p) => { return string.Format("{0} {1}",p.GetString(p.GetOrdinal("FirstName")), p.GetString(p.GetOrdinal("LastName"))); }).Build(); var people = _db.ExecuteSqlStringAccessor<Person>("select * from dbo.Person", mapper); La même chose est possible avec une procédure stockée var people = _db.ExecuteSprocAccessor<Person>("sp_getpeople",mapper); On peut également mapper « ToColumn »
7. DataSet
Méthodes ExecuteDataSet, LoadDataSet, UpdateDataSet
8. Transactions var connection = _db.CreateConnection(); using (connection) { var transaction = connection.BeginTransaction(); try { // transaction.Commit(); } catch (Exception) { transaction.Rollback(); } }
7
7
9. Async
Dans le fichier de configuration
<add name="DefaultConnection" connectionString="Asynchronous Processing=true;Data Source=.\SQLEXPRESS;Initial Catalog=dbPeople;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
TPL
public async Task<IEnumerable<Person>> GetAllAsync() { var people = new List<Person>(); var command = _db.GetSqlStringCommand("Select * from dbo.person"); using (var reader = await Task<IDataReader>.Factory.FromAsync<DbCommand>(_db.BeginExecuteReader, _db.EndExecuteReader, command, null)) { while (reader.Read()) { var person = new Person(); person.Id = reader.GetInt32(reader.GetOrdinal("PersonID")); person.FirstName = reader.GetString(reader.GetOrdinal("FirstName")); person.LastName = reader.GetString(reader.GetOrdinal("LastName")); person.Email = reader.GetString(reader.GetOrdinal("Email")); people.Add(person); } } return people; }
« Ancienne méthode »
private SynchronizationContext synchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext(); public virtual bool GetAllAsync (Action<IEnumerable<Person>> callback) { IEnumerable<Person> response = null; WaitCallback waitCallBack =(param) => { try { response = GetAll(); } catch (Exception ex) { } finally { synchronizationContext.Post((d) => callback((IEnumerable<Person>)d), response); } }; return ThreadPool.QueueUserWorkItem(waitCallBack); }
8
8
public IEnumerable<Person> GetAll() { var people = new List<Person>(); _db.BeginExecuteReader(CommandType.Text, "Select * from dbo.Person", (asyncResult) => { using (var reader = _db.EndExecuteReader(asyncResult)) { while (reader.Read()) { var person = new Person(); person.PersonId = reader.GetInt32(reader.GetOrdinal("PersonID")); person.FirstName = reader.GetString(reader.GetOrdinal("FirstName")); person.LastName = reader.GetString(reader.GetOrdinal("LastName")); person.Email = reader.GetString(reader.GetOrdinal("Email")); people.Add(person); } } }, null); return people; }
_peopleRepository.GetAllAsync((result) => { People = new ObservableCollection<Person>(result); OnPropertyChanged("People"); });
9
9
II. Ado.Net
1. Configuration
Abstraction : System.Data.Common
Chaine de connexion
Provider
On peut définir la chaine de connexion et le provider « en dur » ou dans le fichier de configuration
dans la section « connectionStrings »
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=dbPeople;Integrated Security=SSPI;" providerName="System.Data.SqlClient" /> </connectionStrings>
2. Connexion
a. Création de connexion
Connexion avec chaine et provider définis « en dur »
public DbConnection GetConnection() { var factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); var connection = factory.CreateConnection(); connection.ConnectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=dbPeople;Integrated Security=SSPI;"; return connection; }
Connexion avec chaine définie dans le fichier de configuration (requiert une référence à
System.Configuration)
public DbConnection GetConnection() { var connectionStringsSection = ConfigurationManager.ConnectionStrings["DefaultConnection"]; if (connectionStringsSection == null) throw new ArgumentNullException("connection"); var providerName = connectionStringsSection.ProviderName; var connectionString = connectionStringsSection.ConnectionString; var factory = DbProviderFactories.GetFactory(providerName); var connection = factory.CreateConnection(); connection.ConnectionString =connectionString; return connection; }
b. Ouverture/fermeture/destruction
Il est possible de définir un singleton pour la connexion. Dans ce cas il ne faudra pas détruire la
connexion mais se contenter de la fermer.
10
10
3. Commande
CommandType : texte, procédure stockée using (var command = connection.CreateCommand()) { command.CommandText = "select * from dbo.Person"; // open connection connection.Open(); // # execute command } // destruct command
Une bonne pratique consiste à indiquer le nom des colonnes dans la requête SQL plutôt que d’utiliser *
Création de paramètre command.CommandText = "select * from dbo.Person where PersonID=@id"; command.Parameters.Add(CreateParameter(command, "id", id)); … public DbParameter CreateParameter(DbCommand command, string parameterName, object value) { var parameter = command.CreateParameter(); parameter.ParameterName = parameterName; parameter.Value = value; return parameter; } Il est possible également de définir une méthode d’extension pour la commande.
a. Exécution de la commande et résultat
ExecuteDataReader
ExecuteScalar
ExecuteNonQuery
ExecuteReader using (var reader = command.ExecuteReader()) { while (reader.Read()) { var person = new Person(); person.PersonId = (int)reader["PersonID"]; person.FirstName = (string)reader["FirstName"]; person.LastName = (string)reader["LastName"]; person.Email = (string)reader["Email"]; response.Add(person); } } Test DbNull des colonnes acceptant la valeur « null »
person.Email = reader["Email"] == DBNull.Value ? default(string) :(string)reader["Email"]; ExecuteScalar
var result = command.ExecuteScalar(); int.TryParse(result.ToString(), out response);
11
11
ExecuteNonQuery
response = command.ExecuteNonQuery();
b. Exemples public IEnumerable<Person> GetAll() { var response = new List<Person>(); using (var connection = GetConnection()) { using (var command = connection.CreateCommand()) { command.CommandText = "select * from dbo.Person"; // open connection connection.Open(); using (var reader = command.ExecuteReader()) { while (reader.Read()) { // construct object var person = new Person(); person.PersonId = (int)reader["PersonID"]; person.FirstName = (string)reader["FirstName"]; person.LastName = (string)reader["LastName"]; person.Email = (string)reader["Email"]; response.Add(person); } } // destruct reader } // destruct command } // destruct connection return response; }
public int Count() { var response = default(int); using (var connection = GetConnection()) { using (var command = connection.CreateCommand()) { command.CommandText = "select count(*) from dbo.Person"; // open connection connection.Open(); var result = command.ExecuteScalar(); int.TryParse(result.ToString(), out response); } // destruct command } // destruct connection return response; }
public int Delete(int id) { var response = default(int);
12
12
using (var connection = GetConnection()) { using (var command = connection.CreateCommand()) { command.CommandText = "Delete from dbo.Person where PersonID=@id"; command.Parameters.Add(CreateParameter(command, "id", id)); // open connection connection.Open(); response = command.ExecuteNonQuery(); } // destruct command } // destruct connection return response; }
c. Asynchronisme
TPL
public async Task<IEnumerable<Person>> GetAllAsync() { var response = new List<Person>(); using (var connection = GetConnection()) { using (var command = connection.CreateCommand()) { command.CommandText = "select * from dbo.Person"; await connection.OpenAsync(); using (var reader = await command.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { // construct object var person = new Person(); person.PersonId = (int)reader["PersonID"]; person.FirstName = (string)reader["FirstName"]; person.LastName = (string)reader["LastName"]; person.Email = (string)reader["Email"]; response.Add(person); } } } } return response; }
13
13
d. Transaction public int Delete(int id) { var response = default(int); using (var connection = GetConnection()) { connection.Open(); var transaction = connection.BeginTransaction(); try { using (var command = connection.CreateCommand()) { command.Transaction = transaction; command.CommandText = "Delete from dbo.Person where PersonID=@id"; command.Parameters.Add(CreateParameter(command, "id", id)); response = command.ExecuteNonQuery(); } transaction.Commit(); } catch (Exception) { transaction.Rollback(); } } return response; }
TransactionScope (requiert une référence à System.Transactions) public int Delete(int id) { var response = default(int); try { using (var scope = new TransactionScope()) { using (var connection = GetConnection()) { using (var command = connection.CreateCommand()) { command.CommandText = "Delete from dbo.Person where PersonID=@id"; command.Parameters.Add(CreateParameter(command, "id", id)); connection.Open(); response = command.ExecuteNonQuery(); } } scope.Complete(); } } catch (Exception) { } return response; }
L’utilisation du design pattern Repository est recommandée pour la couche d’accès aux données. EasyDb : une librairie réduisant le volume de code à écrire.
14
14
III. SQL
Le SQL est insensible à la casse, mais pas le nom des tables, colonnes,etc.
1. Création de tables
Utiliser une base de données use [Northwind];
a. Table create table [Products] ( [ProductId] int not null identity (1,1), [CategoryId] int not null default 1, [ProductName] char(50) not null );
Suppression drop table [dbo].[Products]
b. Colonnes
Ajout alter table [dbo].[Products] add [UnitPrice] money not null
Suppression
alter table [dbo].[Products] drop column [UnitPrice]
c. Contraintes
Clé primaire A la création comme exemple de la table du dessus
Ou alter table [Products] add constraint PK_Products primary key ([ProductId]);
Clé étrangère A la création de la table [CategoryId] int not null references [dbo].[Categories]([CategoryId]),
Ou alter table [Products] add constraint FK_Products_Categories FOREIGN KEY ([CategoryId]) references [dbo].[Categories]([CategoryId]);
d. Commentaires --select * --from [dbo].[Person] /* */
15
15
2. CRUD Insérer, modifier, supprimer des données
a. Insert insert into [dbo].[Person]([FirstName],[LastName],[CategoryID]) values('Marie','Bellin',1);
Select into select * into [dbo].[Friend] from [dbo].[Person]
Insert into select insert into [dbo].[Person]([FirstName],[LastName],[CategoryID]) select [FirstName],[LastName],[CategoryID] from [dbo].[Client]
b. Update update [dbo].[Person] SET [CategoryID]=1 WHERE [PersonID]=2
c. Delete delete from [dbo].[Person] where [PersonID]=2
Supprimer toutes les lignes de la table delete from [dbo].[Person]
3. Requêtes Select
a. Select select [FirstName],[LastName] from [dbo].[Person]
Toutes les colonnes select * from [dbo].[Person];
Colonnes de plusieurs tables select [dbo].[Person].[CategoryID],[CategoryName] from [dbo].[Person],[dbo].[Category]
Champs calculés
Exemple : tous les noms de produits entre parenthèses select'(' + [ProductName] + ')' from [dbo].[Products]
Alias select [ProductName] as Beverages from [dbo].[Products] where [CategoryID]=1
select p1.ProductName as beverages,p2.ProductName as condiments from [dbo].[Products] p1,[dbo].[Products] p2 where p1.CategoryID=1 and p2.CategoryID=2
16
16
4. Trier – order by select [FirstName],[LastName],[Email] from [dbo].[Person] order by [FirstName],[LastName]
Tri inversé select * from [dbo].[Person] order by [LastName] desc
5. Filtrer – where select * from [dbo].[Person] where [PersonID]=1
Is null + is not null select * from [dbo].[Person] where [Email] is null;
Between
Entre deux valeurs use [Northwind]; select * from [dbo].[Products] where [UnitPrice] between 0 and 10
In
Des valeurs spécifiées
Exemple sélectionne tous les produits de « CatégoryID » 1 ou 2 ou 3 select * from [dbo].[Products] where [CategoryID] in(1,2,3)
Not
Exemple sélectionne tous les produits, sauf ceux de « CategoryID » 3 select * from [dbo].[Products] where not [CategoryID] =3
Like select * from [dbo].[Products] where [ProductName] like 'C%'
Autre exemple : sélectionne tous les produits dont le nom commence par B ou C select * from [dbo].[Products] where [ProductName] like '[BC]%'
Grouper – group by .. having select count(*) as total from [dbo].[Products] group by [CategoryID] having count(*) <10
17
17
6. Jointures
Sur clé étrangère select [LastName],[CategoryName] from [dbo].[Person],[dbo].[Category] where [dbo].[Person].[CategoryID] = [dbo].[Category].[CategoryID]
Equivalent inner join select [LastName],[CategoryName] from [dbo].[Person] INNER JOIN [dbo].[Category] on [dbo].[Person].[CategoryID] = [dbo].[Category].[CategoryID]
Outer join joint les colonnes qui sont « null », left outer join et right outer join ..
select * from [dbo].[Products] inner JOIN [dbo].[Category] on [dbo].[Products].[CategoryID] = [dbo].[Category].[CategoryID]; select * from [dbo].[Products] left outer JOIN [dbo].[Category] on [dbo].[Products].[CategoryID] = [dbo].[Category].[CategoryID]; select * from [dbo].[Products] right outer JOIN [dbo].[Category] on [dbo].[Products].[CategoryID] = [dbo].[Category].[CategoryID];
Inner join
Left outer join
renvoie les produits n’ayant
pas de « CategoryID »
Right outer join
Renvoie les catégories
pour lesquelles il n’y a pas
de correspondance
Category Products
18
18
7. Procédures stockées create procedure sp_getproducts as select * from [dbo].[Products] go
Avec paramètre
create procedure sp_getproduct(@Id int) as select * from [dbo].[Products] where [ProductId]=@Id go
8. Transactions begin transaction delete from [dbo].[Products] rollback; commit transaction
Exemple begin transaction insert into [dbo].[Category]([CategoryName])VALUES('new category') if @@error <>0 rollback insert into [dbo].[Products]([CategoryId],[ProductName]) values(1,'new product'); if @@error <>0 rollback commit transaction
9. …
Opérateurs <> différent de != différent de < <= > >= !> pas supérieur à !< pas inférieur à And or pour plusieurs conditions
Fonctions Fonctions de texte ,de date et heure,numériques Fonctions d’agregation : AVG(),COUNT(),MAX(),MIN(),SUM() Union et union all Distinct pour éviter les doublons
PL/SQL T-SQL PLAGE/EXEMPLE C#
tinyint tinyint 0 à 255 byte (System.Byte)
smallint smallint -32 768 à 32 767 short(System.Int16)
int int -2 147 483 648
à 2 147 483 647
int(System.Int32)
bigint bigint -9 223 372 036 854 775 808 à 9 223 372 036 854 775 807
long(System.Int64)
numeric(5,2) numeric(5,2) Exemple : 40,77 float(System.Single)
ou double/voir string
19
19
IV. SQL Server
1. Sauvegarde / Restauration de base de données
Sauvegarde
Sur la base de données .. menu Tâches > Sauvegarder
decimal(5,2) decimal(5,2) Exemple : 40,77 float(System.Single)
ou double/voir string
float,real float double (System.Double)
bool bit bool(System.Boolean)
char(50) char(50) de 1 à 4000 caractères string(System.String)
varchar(100) varchar(100) de 1 à 8000 caractères string(System.String)
national char(1000)
nchar(1000) de 1 à 8000 caractères string(System.String)
text,longtext text string(System.String)
date date Exemple : 2003-10-30 DateTime(System.DateTime)
time Time(7) Exemple : 13:00:00 DateTime(System.DateTime)
datetime datetime Exemple : 2003-10-30 13:00:00
DateTime(System.DateTime)
timestamp timestamp TimeSpan(System.TimeSpan)
varbinary(100), blob
varbinary(100)
de 1 à 8000 byte[](System.Byte[])
longblob image byte[](System.Byte[])
On fait dans un premier temps une « sauvegarde complète » puis des « sauvegardes
différentielles » (prenant en compte seulement les changements)
Il est possible également de générer le
Script avec le bouton Script
21
21
2. Sécurité
3 étapes :
- Création Login
- Mappage
- Permissions
Créer un Login
Connexions ...Nouvelle connexion
2 Possibilités :
authentification Windows
ou Authentification SQL Server
23
23
On retrouve désormais l’utilisateur pour la base Northwind. Attention toutefois il n’a pas encore de
permissions et donc ne peut pas lire ou écrire sur la base.
Permissions
Il est possible d’attribuer un rôle au niveau du serveur. Syadmin octroyant tous les droits sur
tous les objets du serveur.
24
24
On peut soit définir très précisément les permissions..
Double clic sur l’utilisateur dans l’onglet sécurité de la base désirée (Northwind pour l’exemple)
Exemple : (Onglet Eléments sécurisables) on donne accès à l’utilisateur uniquement à la table
Products en Lecture (Sélectionner)
A noter que dbo donne accès à tous les objets de la base en lecture, écriture.