data access - romagny13romagny13.com/wp-content/uploads/2015/04/data-overview.pdf · requêtes...

26
DATA ACCESS Tour d’horizon Jérôme Romagny Résumé Ce document est évolutif et sera mis à jour régulièrement

Upload: vutu

Post on 31-Aug-2018

219 views

Category:

Documents


0 download

TRANSCRIPT

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

20

20

Restauration

Sur la base de données .. menu Tâches > Restaurer

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

22

22

Mappage

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.

25

25

Soit donner un rôle plus généraliste à l’utilisateur (Onglet Appartenance). Exemple

db_owner (ou db_datareader,etc.) donne accès à tous les objets de la base. A éviter en

phase de production.

On se connecte avec le compte que l’on vient de créer

On n’a accès qu’à la table Products en lecture