Sunteți pe pagina 1din 17

Bonnes pratiques pour les accs aux donnes

par Johann Blais


Date de publication : 06/02/08
Dernire mise jour : 06/02/08
Cet article a deux objectifs :
1 Prsenter quelques manires d'crire un code gnrique pour les accs
aux donnes (.NET 1.1 et 2.0)
2 Rappeler et expliquer les avantages des paramtres SQL dans le cadre
des requtes SQL (et des procdures stockes).
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 2 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
I - Introduction
II - Abstraction des classes en .NET 1.1
III - Abstraction des classes en .NET 2.0
IV - Utilisation de requtes paramtres
IV-A - Le problme
IV-B - La solution
V - Conclusion
VI - Remerciements
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 3 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
I - Introduction
Les bases de donnes sont aujourd'hui monnaie courante, et ce depuis un certain temps d'ailleurs. Avec chaque
nouveau langage, arrive son lot de nouvelles fonctions, de nouvelles manires de travailler avec des bases.
Nous (dveloppeurs, analystes, etc) avons en nous ce qui mon avis est notre plus grande qualit et notre plus
grand dfaut, nous sommes paresseux. Avant de me jeter des cailloux, laissez moi dvelopper un peu sur ce point.
Vous vous tes tous dit un jour ou l'autre, "j'aimerais bien faire a, mais bon il y a srement dj quelqu'un qui a
eu la mme ide, et qui, dans sa grande bont, a dcid d'en faire profiter la communaut"; on peut d'ailleurs y voir
l'origine du "Google is your friend". Disons les choses comme elles sont, dans ce cas, tre fainant c'est bien, c'est
mme trs bien.
Mais, car il y a toujours un mais, cette qualit se retourne souvent contre nous. J'en prends pour exemple ce collgue
que nous avons tous, celui qui fait partie des dinosaures, a connu l'informatique ses dbuts, pluchait les listings
en LISP et en ADA des magazines de l'poque; je suis sr que vous voyez de qui je parle. Et bien, ce collgue dont
vous riez en cachette (ou ouvertement pour les plus tmraires), est un feignant dans le mauvais sens, il a gard
ses principes de dveloppement ant-diluviens.
Le problme est que l'on a tendance prendre nos petites habitudes, assez vite les bonnes et malheureusement
trs vite les mauvaises.
J'cris cet article avec pour objectif de vous donner quelques trucs, quelques voies emprunter pour vous faciliter
la vie ou plutt le dbogage. Car avoir les bonnes mthodes ds le dbut vous permet de gagner en rapidit, et
surtout en efficacit.
Je commencerai par vous parler d'abstraction des mthodes d'accs, et je terminerai par les bonnes mthodes pour
excuter une requte en fonction de la situation.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 4 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
II - Abstraction des classes en .NET 1.1
Nombre d'entre vous ont dj dvelopp des projets impliquant plusieurs sources de donnes, comme par exemple
une base Oracle d'un ct, une base SqlServer de l'autre, et souvent plusieurs bases de chaque. Vous savez donc
combien il peut tre fastidieux de toujours avoir ouvrir une connexion, crer une requte, l'excuter, parcourir les
donnes et... recommencer avec la base suivante. Car la plupart du temps, vous devez rcuprer de nombreuses
donnes en mme temps et plusieurs endroits.
Rsultat, vous avez cinquante lignes de code qui servent rcuprer les donnes, et vous n'avez mme pas encore
effleur l'aspect mtier de la mthode. Cette approche peut fonctionner dans le cadre d'un projet "Papa/Maman",
mais dans la vraie vie, vous en conviendrez, ce n'est pas ce qu'on peut qualifier de "professionnel".
Pour ceux d'entre vous qui s'impatientent, j'en viens ce qui vous intresse : le CODE. Je ne vais cependant pas vous
submerger avec des lignes et des lignes n'en plus finir. Mais intressons nous certaines structures disponibles
dans le framework.
Les framework .NET (toutes les versions) possdent dans le namespace System.Data, toute une collection
d'interfaces destines nous faciliter la vie. Voici la liste des plus utiles :
a IDbConnection
b IDbCommand
c IDbParameter
d IDbTransaction
e IDbDataAdapter...
La plupart d'entre vous auront dj reconnu les types d'objet qu'ils manipulent quotidiennement mais dans leur forme
concrte (SqlConnection, OracleDataReader, etc). Quel est alors l'intrt pratique d'utiliser les interfaces plutt que
les implmentations. Prcisons d'abord que mme en utilisant les interfaces, il faut bien un moment ou un autre
leur associer une implmentation (SqlServer, Oracle, autre), mais nous reviendrons sur ce point plus tard.
L'utilisation des interfaces permet d'crire un code qui ne dpend pas de la base de donnes sur laquelle il sera
excut. Je suis sr que certain(e)s d'entre vous ont dj eu changer de base de donnes en cours de projet. Si
on n'est pas bien prpar, a peut vite tourner la dpression nerveuse. Passons de suite un exemple concret.
Vous tes un dveloppeur prvoyant, vous avez dj cr une classe qui vous renvoie une commande initialise
partir d'une chane de caractres contenant le code SQL, et comme vous tes encore plus prvoyant, vous passez
aussi votre mthode une enum qui permet mme de slectionner la source de donnes de destination. Vous avez
donc une mthode qui a cette tte l :
public SqlCommand CreateCommand(string commandText, connectionType dataSource);
L j'ai envie de vous dire "c'est pas mal", c'est dj mieux que ce qu'crivent 90% des dveloppeurs.
Vous utilisez donc joyeusement cette mthode partout dans votre code, et vous avez raison. Le problme c'est que
comme tout le monde, vous avez un chef qui a tendance courir en agitant les bras dans tous les sens au moindre
problme. Et l justement le problme c'est que SqlServer, on en a un peu assez, les clients veulent du Oracle.
Qu' cela ne tienne, vous migrez votre base, et vous attaquez maintenant modifier votre code. Vous transformez
videmment votre prcieuse mthode en :
public OracleCommand CreateCommand(string commandText, connectionType dataSource);
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 5 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
Et l, c'est le drame, il faut modifier le code tous les endroits qui appellent cette mthode (heureusement il n'y en
a que 1500), sans compter les SqlDataReader, SqlParameter, etc modifier par la mme occasion.
a fait mal. Revenons au moment de la cration de la mthode Flashback. Vous savez que OracleCommand et
SqlCommand implmentent l'interface IDbCommand. Au lieu d'utiliser un type spcifique, vous allez modifier votre
PM (prcieuse mthode) pour qu'elle renvoie un IDbCommand la place. La signature de votre PM devient :
public IDbCommand CreateCommand(string commandText, connectionType dataSource);
Je prcise aussi que vous ne modifiez rien d'autre dans la PM, simplement le type de retour dans la signature. Vous
utilisez votre PM aux mmes endroits et comme vous utilisez des IDbCommand dans la PM, vous continuez sur votre
lance, et utilisez des IDbParameter et des IDataReader, partout o il faut.
On revient au point critique, le changement de base de donnes. Au dbut mme chose, migration de la base de
donnes, etc. Et vous en arrivez aussi au moment de modifier votre code. Vous commencez aussi par votre PM. Le
type de retour ne change pas, OracleCommand implmente aussi IDbCommand. Il suffit de remplacer dans le corps
de la PM, les objets Sql* par des objets Oracle*.
Et l, miracle, c'est tout. Tout fonctionne exactement pareil. Pas de rechercher/remplacer sauvage, pas de prise de
tte, changements rduits au minimum. Bref votre chef est content !
A ce moment, vous avez fait un constat encore plus accablant, le changement de base de donnes peut n'avoir pour
consquence que le changement de l'objet connexion. En effet, on peut crer une commande en utilisant la mthode
CreateCommand de l'interface IDbConnection. En admettant que vous soyez familier avec le design pattern "factory",
vous avez compris que le changement de base de donnes peut n'avoir comme un impact que l'ajout d'un case dans
la mthode qui cre la connexion en fonction de la source de donnes.
Voici un exemple plus complet :
public IDbConnection CreateConnection(connectionType type)
{
IDbConnection connection = null;
switch(type)
{
case connectionType.MaBaseOracle :
connection = new OracleConnection(maChaineDeConnexion);
case connectionType.MaBaseSqlServer :
connection = new SqlConnection(maChaineDeConnexion);
}
return connection;
}
Pour ceux qui ne veulent pas forcment grer plusieurs sources de donnes, il suffit de simplement de supprimer le
switch et de retourner directement la SqlConnection (ou OracleConnection, OleDbConnection).
Voici un exemple de ce que pourrait tre votre PM :
public IDbCommand CreateCommand(connectionType type, string commandText)
{
// Ajouter la gestion des cas d'erreur
return CreateConnection(type).CreateCommand();
}
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 6 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
Je ne vais pas trop dtailler les diffrentes interfaces, vous avez saisi le principe, je vais simplement rappeler quelques
mthodes utiles :
// Cre un DataReader
IDataReader reader = maCommande.ExecuteReader();
...
// Cre un IDataParameter pour une requte
IDataParameter param = maCommande.CreateParameter();
Pour plus d'informations la documentation du framework contient tout ce dont vous avez besoin.
Mme si cette question concerne principalement le framework 1.1, l'approche utilise est
applicable pour les versions suprieures du framework. Cependant partir de la version
2.0, nous avons notre disposition des outils plus puissants. Nous allons tout de suite
en parler.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 7 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
III - Abstraction des classes en .NET 2.0
Nous venons de voir de quelle manire crire du code gnrique en .NET 1.1 en utilisant les interfaces du namespace
System.Data. A partir du framework 2.0, un namespace a t considrablement mis jour : System.Data.Common.
En version 1.1, ce namespace ne contenait que quelques classes parmi lesquelles DataAdapter, pour ne citer que
la plus utilise. Voyons sans plus attendre les nouveauts de System.Data.Common en .NET 2.0.
En faisant un tour dans la MSDN, on peut avoir une vue d'ensemble des classes ajoutes. La majorit des classes
est nomme Db[quelquechose], ceci donne dj une vague ide de leur utilit, en un mot, cela concerne les bases
de donnes (DB), mais ne donne pas d'indication sur le SGBD en question ( l'inverse des OracleConnection ou
SqlCommand auxquels nous sommes habitus).
Toutes ces classes sont en effet des classes gnriques destines tendre l'utilisation des interfaces. La premire
chose remarquer est que les classes gnriques implmentent ces mmes interfaces, ce qui explique que le code
crit dans la section prcdente fonctionne en l'tat en .NET 2.0, ce qui est une bonne chose lors des migrations
vers le framework 2.0.
La principale nouveaut concerne la notion de "provider" (fournisseur). Cette notion est matrialise par les classes
DbProviderFactories et DbProviderFactory.
Pour la petite information, DbProviderFactories est une " usine abstraite " ou " Abstract Factory " (en anglais, a donne
l'air de mieux s'y connatre). Il s'agit d'un design pattern, c'est en quelque sorte une super Factory. L'ide de l'abstract
factory est d'encapsuler un groupe de factory ayant un thme commun. L'objectif dans le cas de DbProviderFactory
est de pouvoir obtenir une factory dpendante de la source de donnes sans pour autant devoir connatre le type
mme de cette factory.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 8 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
Illustration 1: Schma de l'abstract factory DbProviderFactory
DbProviderFactories permet d'numrer les fournisseurs disponibles pour l'application (ex : SQL Server, Oracle,
MySQL, SQL Server Compact Edition, etc). Elle est aussi responsable de la cration des gnrateurs lis une
source de donnes spcifique.
DbProviderFactory reprsente un gnrateur d'objets lis un fournisseur donn. La classe contient des mthodes
pour crer des DbCommand, DbConnection, DbParameter. Ces objets sont dpendant du fournisseur, cela signifie
que la mthode CreateCommand va, en fonction de la source de donnes, instancier un objet DbCommand avec
une SqlCommand ou une OracleCommand. C'est exactement le mme fonctionnement que les classes que nous
avions cres pour le framework 2.0, sauf que cette fois c'est dj prvu dans le framework.
Passons tout de suite une explication plus pratique. Je vous ai dit juste avant que DbProviderFactories permet
d'numrer les fournisseurs disponibles, en ralit, cette classe va lire les paramtres dfinis dans le fichier
machine.config. Ce dernier contient dans la section <system.data> une balise <DbProviderFactories> qui dfinit un
par un les fournisseurs installs.
Exemple :
<system.data>
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 9 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
<DbProviderFactories>
<add name="SqlClient Data Provider"
invariant="System.Data.SqlClient"
support="FF"
description=".Net Framework Data Provider for SqlServer"
type="System.Data.SqlClient.SqlClientFactory, System.Data,
Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<add name="Odbc Data Provider"
invariant="System.Data.Odbc"
support="BF" description=
".Net Framework Data Provider for Odbc"
type="System.Data.Odbc.OdbcFactory, System.Data,
Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
... etc ...
</DbProviderFactories>
</system.data>
Pour rcuprer cette liste, il suffit d'appeler la mthode DbProviderFactories.GetFactoryClasses() qui renvoie une
DataTable contenant les donnes des fournisseurs. Cela vous permet de savoir dans votre application quelles sont
les bibliothques d'accs aux bases de donnes disponibles l'excution. Vous pouvez ensuite instancier la factory
associe au provider en utilisant la mthode DbProviderFactories.GetFactory(...), cette mthode prend en paramtre
le nom du provider (System.Data.SqlClient par exemple) ou bien une DataRow issue du DataTable prcdemment
cit.
Une fois votre DbProviderFactory cr, vous pouvez utiliser ses mthodes CreateConnection, CreateCommand etc
pour obtenir des instances des classes lies la source de donnes.
Ceci n'est pas le plus utile pour la plupart des dveloppeurs. En ralit, cette fonctionnalit est surtout utile pour les
dveloppeurs d'add-ins et de librairies orientes "accs aux donnes". Le plus important pour le dveloppeur est
la possibilit d'utiliser les DbProviderFactory pour crire un code gnrique. Il permet de remplacer les interfaces
utilises dans l'exemple prcdent par des classes abstraites. Le principal avantage est que les classes gnriques
Db* permettent de grer tous les types d'accs aux donnes : mode connect et dconnect. Ce n'tait pas possible
avec les interfaces IDb* car il n'existait pas de DataAdapter gnrique.
Voici un exemple de ce quoi pourrait ressembler votre code :
public class DataProvider
{
private static DbProviderFactory factory =
DbProviderFactories.GetFactory("System.Data.SqlClient");
public static DbConnection CreateConnection()
{
DbConnection connection = factory.CreateConnection();
connection.ConnectionString =
ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
connection.Open();
return connection;
}
public static DbCommand CreateCommand(string commandText)
{
DbCommand command = CreateConnection().CreateCommand();
command.CommandText = commandText;
return command;
}
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 10 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
public static DbDataReader CreateDataReader(string commandText)
{
return
CreateCommand(commandText).ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
public static DbDataReader CreateDataReader(DbCommand command)
{
return command.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
public static DataSet CreateDataSet(string commandText)
{
DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = CreateCommand(commandText);
DataSet ds = new DataSet();
adapter.Fill(ds);
adapter.SelectCommand.Connection.Close();
return ds;
}
}
Ce code n'est qu'un trs simple exemple de ce qu'il est possible de faire avec les classes gnriques. A la diffrence
de l'exemple en .NET 1.1, celui ci se focalise sur l'criture d'un code non dpendant de la source de donnes, alors que
le premier exemple mettait l'accent sur l'utilisation de plusieurs sources de donnes de manire quasi transparente.
En utilisant ces techniques, vous tes maintenant capables d'crire un code non dpendant de la source de donnes
et/ou apte grer plusieurs sources de donnes de type diffrent en parallle.
Passons maintenant une autre bonne pratique en ce qui concerne l'interrogation d'une base de donnes. Cette fois
ci, cela ne concerne pas le type de la source de donnes mais plutt la manire de l'interroger.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 11 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
IV - Utilisation de requtes paramtres
La plupart d'entre vous construit les requtes SQL en les concatnant morceau par morceau en insrant un morceau
de SELECT par-ci, un morceau de FROM par-l et encore un bout de WHERE au passage. La plupart s'en satisfait
ainsi, ne se posant pas la question de savoir s'il agit de la bonne mthode, et encore moins s'il en existe une autre.
Et dans la plupart des cas, a fonctionne, enfin disons plutt, a ne plante pas trop.
Alors vous avez peut-tre dj entendu quelqu'un parler de ce que l'on appelle les requtes paramtres (RP). Pour
mettre les choses au point tout de suite, les RPs existent depuis longtemps, bien longtemps, alors que .NET n'tait
pas prs de voir le jour. Il s'agit d'une fonctionnalit peu utilise voire mconnue de la plupart des gens (sauf les
DBA, en considrant en plus que les DBA soient des gens, mais c'est un autre problme).
IV-A - Le problme
Avant d'tudier ce qu'est rellement une RP en .NET, attardons nous sur une requte classique crite par un
dveloppeur lambda. L'objectif de la requte est d'extraire de la table commandes, les identifiants des commandes
enregistres par un certain vendeur depuis une certaine date. Voici comment se prsente la requte dans la plupart
des cas :
SELECT id FROM commandes WHERE vendeur = [NOM] AND date > [DATE]
Les valeurs entre crochets indiquent les endroits o il faut insrer une valeur spcifique. Ce qui donne dans la plupart
des cas lors de la premire criture :
string nom = "Georges";
DateTime date = DateTime.Today.AddDays(-7);
IDbCommand maCommande = maConnexion.CreateCommand();
maCommand.CommandText = "SELECT id FROM command WHERE login=" + nom +
" AND date > " + date.ToString("dd/MM/yyyy HH:mm:ss");
Premire excution de la requte, a explose. La raison, le format de la date, il y a un espace au milieu. Etant
spcialiste SQL, vous ajoutez des apostrophes autour de la date, et vous transformez l'instruction en :
string nom = "Georges";
DateTime date = DateTime.Today.AddDays(-7);
IDbCommand maCommande = maConnexion.CreateCommand();
maCommand.CommandText = "SELECT id FROM command WHERE login='" + nom +
"' AND date > '" + date.ToString("dd/MM/yyyy HH:mm:ss") + "'";

Deuxime excution, re-plantage, cette fois ci, c'est le format de la date qui est en cause. En effet le serveur est dans
une culture anglo-saxone et le format de la date est mois/jour/anne. Qu' cela ne tienne :
string nom = "Georges";
DateTime date = DateTime.Today.AddDays(-7);
IDbCommand maCommande = maConnexion.CreateCommand();
maCommand.CommandText = "SELECT id FROM command WHERE login='" + nom +
"' AND date > '" + date.ToString("yyyy-MM-dd") + "'";

Bonnes pratiques pour les accs aux donnes par Johann Blais
- 12 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
Cette fois, la date fonctionne plus ou moins, mais a plante encore. Vous aurez remarqu que j'en ai profit pour
changer le nom car, problme, tout le monde ne s'appelle pas Georges. Et dans ce cas, notre vendeur est Irlandais
(pourquoi pas), et il s'appelle O'Hara. Vous vous doutez dj de ce qui s'est pass, l'apostrophe dans le nom a encore
fait exploser la requte, nous avons une droit une belle erreur de syntaxe.
Vous avez tous dj eu ce problme (pas les Irlandais, mais les apostrophes). Encore une fois, une nouvelle
correction, vous tes toujours un pro de SQL et vous avez tout de suite pens "je vais 'chapper' les apostrophes".
Soit :
string nom = "O'Hara";
DateTime date = DateTime.Today.AddDays(-7);
IDbCommand maCommande = maConnexion.CreateCommand();
maCommand.CommandText = "SELECT id FROM command WHERE login='" +
nom.Replace("'", "''") +
"' AND date > '" + date.ToString("yyyy-MM-dd") + "'";
Victoire, cette fois, a fonctionne. Enfin, j'ai envie de dire que a ne plante plus, et encore a me semble optimiste.
Si vous avez un tant soit peu d'amour propre, vous avez honte de ce code, et je dois avouer que je vous comprends.
Il doit srement y avoir une meilleure solution. Finalement, une utilit pourrait tre trouve pour ces RP. Vous avez
eu raison de garder espoir.
IV-B - La solution
Concrtement, qu'est ce qu'une requte paramtre ? Il s'agit d'une requte qui contient des variables la place de
certaines valeurs. Vous pouvez sans problme crire une requte paramtre dans votre diteur SQL prfr.
Voici une requte exemple :
SELECT id FROM commandes WHERE vendeur = @p_name AND date > @date_effet
Cette requte extrait de la table commandes, les identifiants des commandes enregistres par un certain vendeur
depuis une certaine date. Dans cet exemple, les deux valeurs pour le login et le mot de passe sont remplaces
par deux paramtres identifis par l'@ qui les prcde. En fonction de votre navigateur SQL (TOAD, SQLNavigator,
Entreprise Manager, SQL Server Management Studio Express, etc), vous serez peut-tre invits saisir des valeurs
pour l'excution de la requte, dans le cas contraire, la requte va srement chouer car les paramtres sont en
fait des variables qui doivent tre dclares plus haut dans le script (ceci n'est valable que pour l'excution dans
un diteur SQL).
Passons maintenant l'criture de ce type de requte en utilisant le framework .NET. L'criture se fait comme pour
une requte traditionnelle :
IDbCommand maCommande = maConnexion.CreateCommand();
maCommande.CommandType = CommandType.Text;
maCommande.CommandText = "SELECT id FROM command WHERE login = @login AND date > @date_effet";
Il suffit ensuite de dclarer deux magnifiques paramtres SQL comme ceci :
IDbParameter paramNom = maCommande.CreateParameter();
paramNom.Name = "@login";
paramNom.DbType = DbTypes.VarChar;
paramNom.Direction = ParameterDirection.Input;
paramNom.Value = "O'Hara";
IDbParameter paramDate = maCommande.CreateParameter();
paramDate.Name = "@date_effet";
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 13 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
paramDate.DbType = DbTypes.DateTime;
paramDate.Direction = ParameterDirection.Input;
paramDate.Value = DateTime.Today.AddDays(-7).Date;
On termine par l'ajout de ces paramtres dans la liste des paramtres de la commande :
maCommande.Parameters.Add(paramNom);
maCommande.Parameters.Add(paramDate);
Il ne reste plus qu' excuter votre requte de manire habituelle.
Pour rsumer, nous venons de :
a Crer une requte avec des "blancs", les paramtres,
b Crer des paramtres SQL dont le nom correspond au nom d'un des paramtres,
c Affecter un type et une valeur correspondante chaque paramtre,
d Associer les paramtres notre requte,
e Excuter la requte sur la source de donnes sans se ramasser un tas d'erreurs et sans se fatiguer,
f Avoir honte en repensant toutes les bidouilles mises en place avant pour viter tous ces problmes.
L'intrt des paramtres ne se manifeste pas seulement dans le scnario prcdemment dcrit. En effet les
paramtres SQL ne sont pas un concept de dveloppement, mais un concept de base de donnes. Il existe des
raisons supplmentaires d'utiliser des paramtres SQL qui sont cette fois plus lies la source de donnes. Pour
cela intressons nous quelque peu la manire de travailler de nos SGBD prfrs (mme s'il existe des diffrences
entre les diffrents produits, l'ide est la mme chaque fois).
Lors de l'envoi d'une requte au SGBD, la requte est d'abord analyse pour en extraire les paramtres par exemple,
mais aussi les index qui vont tre utiliss, etc. Ces donnes recueillies sont ensuite places (avec la requte) dans un
cache interne. De cette manire, si la mme requte revient, il n'y aura pas ncessit de la r-analyser. Ce systme
fonctionne bien SAUF dans le cas o toutes les requtes sont diffrentes, dans ce cas, le cache va grossir inutilement,
les requtes vont tre analyses chaque fois. Vous tes en train de vous dire " oh mon dieu, c'est horrible ", et
vous avez raison. Vous pensez aussi " Ouf, heureusement que j'utilise souvent la mme requte, je ne tombe jamais
dans ce cas-l ", et l, c'est le drame, vous vous trompez lamentablement ! Car oui, vous tes en fait dans ce cas
horrible qui fait peur au DBA. " Tu mens " me direz vous, et vous allez mme ajouter " quand je fais des insertions
en masse, c'est le mme INSERT chaque fois ". Et bien non, je ne mens pas et je le prouve. Quand vous faites
vos insertions, vous utilisez srement une requte de type :
while (jAiDesDonnneesEnAttente)
{
IDbCommand maCommande = maConnexion.CreateCommand();
maCommande.CommandType = CommandType.Text;
maCommande.CommandText = "INSERT INTO maTable VALUES('" + maDonneeQuiChangeAChaqueIteration +
"')";
maCommande.ExecuteNonQuery();
}
Au final, vous envoyez au SGBD des requtes du genre :
"INSERT INTO maTable VALUES('TOTO')"
"INSERT INTO maTable VALUES('NENESS')"
"INSERT INTO maTable VALUES('JUNIOR')"
Ce sont, vous l'avez maintenant compris, des requtes compltement diffrentes du point de vue du SGBD.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 14 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
Dans le cas prsent, si vous aviez utilis un paramtre SQL pour la valeur enregistrer, la requte aurait t la mme
chaque fois, et uniquement la valeur du paramtre aurait t modifie. Vous pouviez mme crire tout le code de
cration de la requte en dehors de la boucle pour ne laisser dans celle-ci que :
while (jAiDesDonnneesEnAttente)
{
monParametre.Value = maDonneeQuiChangeAChaqueIteration;
maCommande.ExecuteNonQuery();
}
C'est finalement plus lisible/robuste/efficace que la premire mthode.
Abordons maintenant un autre avantage inestimable des paramtres SQL, vous vous mettez l'abri des injections
SQL. Il s'agit d'une attaque courante, principalement pour les applications Web, qui consiste introduire dans un
champ de saisie, une chane reprsentant un morceau de requte SQL qui, si l'application n'est pas protge, va tre
excut dans la source de donnes votre insu. Pour illustrer, reprenons le code prcdent en le modifiant un peu :
IDbCommand maCommande = maConnexion.CreateCommand();
maCommande.CommandType = CommandType.Text;
maCommande.CommandText = "INSERT INTO maTable VALUES('" + monTextBox.Text + "')";
maCommande.ExecuteNonQuery();
Les utilisateurs sympas vont saisir des chaines gentilles comme " bonjour " ou " mon PC il KC ". Ces utilisateurs l
sont comme la piti dans l'oeil d'un percepteur d'impt, ils n'existent pas. Non la place, vous aurez des utilisateurs
mal rass avec les cheveux longs (des mchants utilisateurs) qui vont essayer de saisir des chanes du style " ');
DROP DATABASE; -- ". Au final, votre requte va donner : INSERT INTO maTable VALUES(''); DROP DATABASE;
--')
Au revoir la base de donnes, merci d'avoir jou. Si vous aviez utilis un paramtre SQL, la chane passe dans la
requte aurait t 'chappe' et au final, vous auriez insr sans encombre la valeur saisie dans la zone de texte.
J'en vois un paquet qui sont en train de paniquer, oui, effectivement, il est temps d'aller modifier vos applications
avant qu'il ne soit trop tard. Voil pour la partie SQL Injection.
Encore un autre avantage, en concatnant vos valeurs dans la requte, vous introduisez une source d'erreur, une
couche d'illisibilit supplmentaire, car vous tes toujours oblig de compter les apostrophes, de savoir o il faut
ajouter les plus, les virgules, les parenthses qui manquent, etc. En utilisant les paramtres, vous avez la possibilit
de crer la requte et de la tester dans un outil externe (Entreprise Manager par exemple), et de la copier/coller en
l'tat sans aucune modification dans votre code. Gain de temps en dveloppement et en maintenance future.
Et un petit dernier pour la route, en concatnant la requte, vous passez ct de toute la belle gestion des erreurs
du framework en abandonnant une hypothtique InvalidCastException en dbogage au 'profit' d'une espce d'erreur
SQL gnrique qui ne vous donne aux mieux qu'une apprciation minable de la source de l'erreur. En effet en cas
d'erreur sur un paramtre SQL, vous obtenez des exceptions types comme des InvalidCastException ou autre,
alors qu'en utilisant une requte "concatne", vous n'avez que des exceptions gnriques... Pas terrible pour le
dbogage, non ?
Vous avez donc maintenant une solide connaissance de l'(immense) intrt des paramtres SQL. Il ne vous reste
plus qu' mettre tout a en pratique.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 15 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
V - Conclusion
Nous avons vu au travers de ces diffrents points qu'en matire d'accs au donnes, vous avez votre disposition
toute une srie d'outils qui vous permettent de gagner du temps de dbogage et de maintenance. Il vous reste
maintenant mettre en pratique tous ces concepts pour qu'ils deviennent des automatismes, car ce qui diffrencie
un dveloppeur standard et un bon dveloppeur n'est pas la rapidit de dveloppement, mais le rapport entre la
qualit du code et le temps de dveloppement.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 16 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/
VI - Remerciements
Je pourrais jamais assez remercier Aspic pour toute l'aide qu'il m'a apporte pour la rdaction de cet article. Un autre
grand merci caro95470 pour la correction orthographique de cet article.
Bonnes pratiques pour les accs aux donnes par Johann Blais
- 17 -
Ce document est issu de http://www.developpez.com et reste la proprit exclusive de son auteur. La copie, modification et/ou distribution par
quelque moyen que ce soit est soumise l'obtention pralable de l'autorisation de l'auteur.
http://johannblais.developpez.com/tutoriel/dotnet/bonnes-pratiques-acces-donnees/

S-ar putea să vă placă și