samedi 23 mai 2009

Windows 7 : Windows XP Mode

  Une autre grande nouveauté de Windows Seven vient de l’intégration directe d’une machine virtuelle Windows XP.

   En effet, il vous suffira de télécharger la version Beta de Virtual PC 2007 ainsi que le Windows XP mode Beta 

        Une fois les deux packages installés, Windows configure automatiquement la machine virtuelle, vous pourrez sauvegarder automatiquement les login et password à utiliser avec votre vm XP pour accélérer son démarrage. Ensuite, il ne vous reste plus qu’à installer les applications que vous souhaitez utiliser dans la machine XP, et de créer les raccourcis à la racine du menu Démarrer. les raccourcis ajoutés ainsi deviennent automatiquement visibles dans le menu de la machine hôte (comptez quand même une bonne dizaine de secondes avant que le raccourci apparaisse) .

image

image

     Lorsque vous lancez l’application, la fenêtre de votre logiciel apparaît avec le style windows XP directement dans votre environnement Seven :

image

jeudi 21 mai 2009

Visual Studio 2010 Beta 1 accessible au grand public

 

   ça y est c’est parti, la toute dernière version de Visual Studio 2010 est accessible en libre téléchargement pour le grand public :

2 versions sont accessibles :

les principales nouveautés introduites  consistent en une refonte de l’interface utilisateur de Visual Studio (recodée en WPF), La version bêta du Framework .NET 4.0, des facilités de développement sur la plateforme Azure de Microsoft, et bien d’autres choses encore ! (plus d’infos ici et )

lundi 18 mai 2009

Lire les fichiers de configuration (.INI) en C#

 

     Les fichiers .INI sont des fichiers texte classiques, structurés de la façon suivante :

[SECTION]
Key0 = value_for_this_key
Key1 = value_for_this_key

[SECTION2]
Key0 = value_for_this_key

[....]

    Pour les lire, nous pouvons simplement faire appel aux API standards de Windows contenues dans la DLL Kernel32 :

[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section,string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,string key, string def, StringBuilder retVal,int size, string filePath);


    L’utilisation de ces méthodes est très intuitive.  Prenons comme exemple les fichiers favoris d’Internet Explorer.  Ces fichiers ont une extension .URL, mais leur structure est celle d’un fichier de configuration classique. considérons le fichier suivant :

[DEFAULT]
BASEURL=http://thoums.blogspot.com/
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2
[InternetShortcut]
URL=http://thoums.blogspot.com/
IDList=
IconFile=http://thoums.blogspot.com/favicon.ico
IconIndex=1

   Pour lire le champ BASEURL il suffit d’écrire :

StringBuilder valeur = new StringBuilder(32768);
GetPrivateProfileString("DEFAUT", "BASEURL", "", valeur, valeur.MaxCapacity, @"C:\MyFavorite.URL");
Console.WriteLine(valeur.ToString());

     L’écriture se fait de la même manière. Notez que si la section dans laquelle vous souhaitez ajouter une clé n’existe pas, elle sera crée, de même que la clé. Par ailleurs si vous tentez d’écrire dans un fichier qui n’existe pas, il sera également créé. Cette méthode n’est pas liée à l’extension du fichier, tout fichier texte ayant une structure [Section] clé=valeur,  pourra être traité de cette façon.

dimanche 17 mai 2009

“Le fournisseur de données .Net Framework demandé est introuvable. Il n'est peut-être pas installé.”

 

     J’ai eu cette erreur en travaillant sur une base de donnée Oracle avec le client Oracle 11g ODAC 11.1.0.6.21. Pour une raison que j’ignore encore, le provider Oracle pour ADO .NET ne s’était pas inscrit dans mon fichier machine.config. N’ayant pas trop envie de modifier ce fichier sans connaître les répercutions précises que ça pourrait avoir, la solution intermédiaire que j’ai trouvée est de rajouter l’enregistrement suivant dans le fichier App.Config de mon application :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.data>
<DbProviderFactories>
<add name="Oracle Data Provider for .NET Version 11.1.0.6.0"
invariant="Oracle.DataAccess.Client"
description="Oracle Data Provider for .NET Version 11.1.0.6.0"
type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess,
Version=2.111.6.0, Culture=neutral, PublicKeyToken=89b483f429c47342"
/>
</DbProviderFactories>
</system.data>
</configuration>

et là, miracle, tout marche nickel.

Tutoriel ADO .NET : utilisation des DbProviderFactories

(Ce billet fait suite à l’article : Abstraction des Accès BDD dans une Factory )

     Entrons un peu plus dans le détail maintenant que nous avons une vue générale de ce que Microsoft à mis à notre disposition. nous allons donc réaliser une Classe nommée ConnectDb  encapsulant l’utilisation des types vus précédemment :

image 

     La classe ConnectDb contient un ensemble de méthodes statiques qui couvrent les opérations de base pour accéder à une base de données.

    Personnellement j’ai choisi de la définir en abstract puisque toutes ses méthodes sont statiques et qu’elle n’a pas vocation à être instanciée. On aurait pu mettre le constructeur par défaut en privé, le résultat serait le même, je pense que c’est surtout une question de goût…

Voyons en détail l’implémentation de cette classe.

 

 

 

Implémentation

Gestion des paramètres de connexion

    Pour ma part j’ai choisi de stocker la chaîne de connexion, ainsi que le provider choisi dans un fichier de configuration (Pour savoir comment créer votre propre fichier de configuration,vous pouvez consulter ce billet).

    Voici à quoi ressemble mon fichier App.config :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="MyDefaultConnectionString"
connectionString="Data Source=MYSERVER\SQLEXPRESS;Initial Catalog=MyDataBase;Integrated Security=True;Pooling=False"/>
</connectionStrings>
<appSettings>
<add key="MyDefaultProvider" value="System.Data.SqlClient"/>
</appSettings>
</configuration>

     Pour mon exemple j’ai choisi d’utiliser une base de données SQL Server et le provider correspondant : Sytem.Data.SqlClient . Bien entendu, la suite du code fonctionnera avec n’importe quel provider implémentant les Types abstraits du namespace System.Data.Common.

static readonly String _connectionString;
static readonly String _provider;

static ConnectDb()
{
_connectionString = ConfigurationManager.ConnectionStrings["MyDefaultConnectionString"].ConnectionString;
_provider = ConfigurationManager.AppSettings["MyDefaultProvider"].ToString();
}

    J’ai défini un constructeur statique pour récupérer les valeurs de la chaîne de connexion et le provider choisi depuis le fichier de configuration. Un constructeur static est appelé automatiquement à la première référence du type. Il ne nécessite donc pas d’instanciation explicite .

Utilisation de la factory

    La récupération de la factory pour un provider spécifique se fait à l’aide de la classe DbProviderFactories. Pour ma part j’ai choisi de l’encapsuler dans une Propriété “Factory” :

protected static DbProviderFactory Factory
{ get
{
return DbProviderFactories.GetFactory(_provider);
}
}

    A ce stade nous disposons donc d’une instance d’un type implémentant DbProviderFactory, spécifique au provider que nous avons choisi. (dans mon cas il s’agit en interne d’une instance de  System.Data.SqlClient.SqlClientFactory).

    Parmi l’ensemble  des méthodes que nous propose l’objet DbProviderFactory , nous n’en utiliserons que deux dans le cadre de cet article :

//Crée une instance d'un objet de type DbCommand
Factory.CreateCommand();
//Crée une instance d'un objet de type DbConnection
Factory.CreateConnection();

   voyons comment les utiliser :

Connexion à la Base de données

    Rien de bien compliqué ici, on appelle la méthode CreateConnection() de la factory, puis on affecte la Chaîne de connection à l’objet DbConnection que l’on a récupéré.

protected static DbConnection GetConnection()
{
DbConnection conn = Factory.CreateConnection();
conn.ConnectionString = ConnectionString;
return conn;

}

    Attention, l’objet connexion retourné est FERME. la connexion à la base n’est donc pas active. J’ai choisit de ne pas retourné d’objet systématiquement ouvert pour ne pas risquer d’oublier de le refermer. Cela permet donc de garder le contrôle sur le pool de connexion ouvertes.

Manipulation des objets DbCommand

    Création

    Une fois la connexion à la base de donnée gérée, il ne reste plus qu’à envoyer les requêtes. C’est là que l’objet DbCommand entre en jeu. Il y a 2 façons de créer un objet commande: en utilisant la factory, ou en passant par un objet DbConnection :

  • Solution 1
public static DbCommand CreateCommand()
{
DbCommand cmd = Factory.CreateCommand();
return cmd;
}
  • Solution 2
public static DbCommand CreateCommand()
{
DbConnection conn = GetConnection();
DbCommand cmd = conn.CreateCommand();
return cmd;
}

    Pour ma part je préfère la première version. C’est encore une fois assez discutable, la raison est que je préfère garder mes objets indépendants les uns des autres le plus longtemps possible.

Envoi d’une requête

   Une fois notre Objet DbCommand créé nous pouvons lui affecter une requête simple à l’aide de la propriété CommandText (nous verrons par la suite comment faire pour créer des commandes paramétrées") :

DbCommand cmd = ConnectDb.CreateCommand();
cmd.CommandText = "SELECT Champ1, Champ2 FROM MATABLE WHERE Champ3='toto'";

   Attention : La syntaxe des requêtes SQL peut différer d’un SGBD à l’autre, c’est pourquoi, si l’on veut conserver la souplesse de notre factory, l’affectation d’une requête SQL à une DbCommand doit être effectuée EN DEHORS de la classe ConnectDb, par exemple dans une classe de DAO classique.

Nous pouvons regrouper les différents types de requête en 3 catégories :

  • Les requêtes de Sélection qui renvoient un ensemble de données (SELECT .. FROM… JOIN.. WHERE)
  • Les ordres DDL qui renverront la plupart du temps un nombre d’enregistrements affectés (Update, Delete,Insert…)
  • Les requêtes Scalaires qui retournent un champ unique dans un enregistrement unique (ex : “SELECT Count(*) FROM matable”, “SELECT ORACLE_SEQUENCE.NEXTVAL FROM dual”)

    Typiquement nous pouvons donc créer 3 méthodes de requêtage pour gérer les différents cas. Le code de ces méthodes diffère assez peu, dans tous les cas, il nous faut récupérer un objet DbConnection, l’ouvrir et l’affecter à l’objet DbCommand contenant la requête.

Comme un exemple vaut mieux qu’un long discours, voici l’implémentation de ces fonctions :

  • ExecuteQuery (requêtes de Sélection)
public static DbDataReader ExecuteQuery(DbCommand cmd)
{
DbDataReader ret;
DbConnection conn = GetConnection();
conn.Open();
cmd.Connection = conn;
cmd.cr
ret = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);

return ret;
}

    L’objet DbDataReader est un Curseur sur un ensemble d’enregistrement en provenance de la base de donnée en mode connecté, un peu à la manière des vieux recordset. c’est donc dans cet objet que sont renvoyés les enregistrements sélectionnés par la commande.

     Le paramètre passé à la méthodes ExecuteReader() de l’objet DbCommand est une énumération qui  permet de s’assurer que la connexion sera fermée en même temps que le DataReader. En effet, pour que ce dernier fonctionne correctement, la connexion sur laquelle il repose doit rester ouverte jusqu’au dernier moment. On ne peut donc pas fermer la connexion immediatement après la création du DataReader.

  • ExecuteNonQuery (ordres DDL)

public static int ExecuteNonQuery(DbCommand cmd)
{
int ret;
using (DbConnection conn = GetConnection())
{
conn.Open();
cmd.Connection = conn;
ret = cmd.ExecuteNonQuery();
conn.Close();
return ret;
}
}

     Ici le code est assez similaire à la méthode précédente, on notera cette fois la présence du bloc using autour de l’objet DbConnection. En effet Microsoft à eu la bonne idée d’implémenter IDisposable sur ces objets. Ainsi, dans le cas de l’objet DbConnection, on est sûr que quoiqu’il arrive, la méthode Dispose() sera appelée, et que la connexion à la base, fermée.

  • ExecuteScalar (requêtes Scalaires)
public static object ExecuteScalar(DbCommand cmd)
{
object ret;
using (DbConnection conn = GetConnection())
{
conn.Open();
cmd.Connection = conn;
ret = cmd.ExecuteScalar();
conn.Close();
return ret;
}
}

    Encore une fois, le code ne diffère pas énormément de la méthode ExecuteQuery(), la seule chose à noter est que le type de retour de la méthode est de type object. il vous faudra donc penser lors de l’utilisation, à effectuer un cast sur la valeur retournée en fonction du type que vous attendez.

    Maintenant que nous avons nos 3 méthodes, l’exécution d’une requête est simplissime :

ex:

//requête simple
DbCommand cmd = ConnectDb.CreateCommand();
cmd.CommandText = "SELECT * FROM Personne where prenom = ‘toto’";

DbDataReader reader = ConnectDb.ExecuteQuery(cmd);
while (reader.Read())
{
System.Console.WriteLine("Nom : {0} ,Prenom : {1} , mail : {2}",reader["nom"],reader["prenom"],reader["mail"]);
}
reader.Close();

     Le parcours d’un objet DbDataReader se fait grâce à la méthode booléenne Read(). A la construction de l’objet le curseur pointe AVANT le tout premier enregistrement. A chaque appel de Read(), le curseur passe à l’enregistrement suivant, si l’on est arrivé à la fin du DataReader ou si la requête n’a aucun résultat , Read() renvoi False.

    Pour accéder aux champs des enregistrement, vous pouvez soit utiliser la méthode “tableaux” à l’aide de crochets : reader[“nomColonne”] ou reader[NumColonne"], sans oublier de caster correctement les valeurs de retour.

    Ou bien encore utiliser toute les méthodes reader.GetXXXX() pour récupérer directement une valeur typée.

     Nous avons vu qu’il était très simple d’envoyer une requête à la base de données, cependant il peut parfois être utile de paramétrer une requête, c’est à dire, dissocier les valeurs des champs. Cela permet un traitement plus automatisé, plus optimisé et plus sûr des requêtes, notamment au niveau des attaques par injection de code SQL. Voyons ensemble comment écrire de telles requêtes.

Création d’une requête paramétrée

     Créer une requête paramétrée n’est pas bien plus difficile que de créer une requête sans paramètre : reprenons un objet DbCommand, et voyons quelles autres méthodes s’offrent à nous :

     Nous avons une Méthode CreateParameter() et une propriété Parameters qui contient la liste de tous les paramètres associés à cette commande.

    Avant d’associer des paramètres à une DbCommand, il faut tout d’abord référencer ces paramètres dans la requête elle même. cela se fait grâce à un mot clé choisi par vous-même, précédé d’un symbole qui indiquera au provider que la chaîne de caractère suivante est un paramètre. Ce symbole varie d’un provider à l’autre, voici un tableau récapitulant ceux que je connais :

Oracle ‘:’ + NomParametre
SqlServer ‘@’+ NomParametre
Access ‘?’+ NomParametre

    Il nous faut ensuite créer les paramètres à proprement parler et leur donner leur valeur. rien de plus simple, on crée un objet DbParameter à l’aide de la méthode CreateParameter de la DbCommand et on affecte les propriétés suivantes :

  • ParameterName : chaîne de caractères correspondant au paramètre dans la requête (sans le symbole)
  • Value : valeur à affecter au paramètre
  • DbType (facultatif) : Type à affecter au paramètre

Puis on ajoute le paramètre créé à la Liste des paramètres de la commande. A partir de là la commande s’exécute exactement comme une commande normale, à l’aide des 3 méthodes que nous avons définies plus haut.

Exemple d’une requête d’insertion: on suppose que l’on dispose d’une table personne dans notre base de données :

image

//requête Parametrée
DbCommand cmd2 = ConnectDb.CreateCommand();
cmd2.CommandText = "INSERT INTO Personne (mail,nom,prenom)VALUES (@mail,@nom,@prenom)";
DbParameter mailParam = cmd2.CreateParameter();
mailParam.ParameterName = "mail";
mailParam.Value = "uneadressemail@kkpart.com";
cmd2.Parameters.Add(mailParam);




DbParameter nomParam = cmd2.CreateParameter();
nomParam.ParameterName = "nom";
nomParam.Value = "COVILLE";
cmd2.Parameters.Add(nomParam);



DbParameter prenomParam = cmd2.CreateParameter();
prenomParam.ParameterName = "prenom";
prenomParam.Value = "Thomas";
cmd2.Parameters.Add(prenomParam);


ConnectDb.ExecuteNonQuery(cmd2);

    Et voilà, vous disposez désormais d’une classe complète permettant de gérer tout vos accès base en mode connecté, de façon totalement indépendante du SGBD choisi !

    Nous pouvons cependant aller encore plus loin grâce à la généricité. En effet pourquoi se contenter d’utiliser un DbDataReader, quand on peut facilement disposer d’une Liste d’objets directement prêts à l’usage ? C’est ce que nous allons faire de suite :

La Methode Find<T>(…)

Implémentation

    L’objectif de cette méthode, est d’obtenir, en se basant sur une DbCommand on ne peut plus classique, une liste d’objets de type T  (générique donc).

    Pour ce Faire nous allons utiliser la notion de delegate et plus particulièrement le Type Func<Targ0, TResult> ajouté dans le framework 3.5 . Grosso modo, c’est une référence vers une fonction qui prendrait un paramètre de type Targ0 et renverrait un objet de type TResult.

    L’intérêt pour nous, c’est que nous allons donc pouvoir ajouter en paramètre de notre méthode Find, un delegate qui prendrait en Parametre un DbDataReader et renverrait un objet de Type T  remplit avec les données du DbDataReader. Pour faire simple, le delegate serait un “traducteur” pour passer d’un DbDataReader, vers une List<T>.

Voici donc à quoi ressemble notre Méthode Find :

public static List<T> Find<T>(DbCommand cmd, Func<DbDataReader, T> ParseObject)
{
using (DbDataReader rs = ExecuteQuery(cmd))
{
List<T> retList = new List<T>();

while (rs.Read())
{
T oRow = ParseObject(rs);
retList.Add(oRow);
}
return retList;
}
}

   Tout comme le type DbConnection,  le type DbDataReader implémente IDisposable. On ne se privera donc pas de l’entourer d’une directive using, pour s’assurer que les connections à la base seront correctement fermées. A l’aide de la méthode ExecuteQuery() que nous avons codée précédemment, nous récupérons le DbDataReader correspondant à notre DbCommand.

   Puis, pour chaque Enregistrement du DataReader, nous appelons le delegate passé en paramètre à notre méthode find  (Parseobject()). Il ne nous reste plus qu’à ajouter l’objet de Type T à la List<T> et à retourner cette liste une fois le reader entièrement parcouru.

   Très bien me direz vous, mais comment ça s’utilise ce machin ? démonstration :

Utilisation

   Considérons tout d’abord que dans notre Base de données, nous avons une table Personne contenant les champs suivants :

   Reprenons l’exemple de notre table Personne :

image

      Nous réalisons une classe Personne dans notre programme pour la manipulation des données :

public class Personne
{
public String NomPrenom
public String Mail;

}

      J’ai fait exprès de Concaténer dans ma Classe les champs Nom et Prenom et d’omettre le champ Age,  pour bien montrer que les structure des classes du programme peuvent être différente de celles des tables de la base.

nous créons notre requête de sélection :

DbCommand cmd = ConnectDb.CreateCommand();
cmd.CommandText = "SELECT * FROM Personne";

    Il ne reste plus qu’à appeler notre fameuse méthode Find() . Avant cela je dois introduire la notion d’Anonymous Delegate. Grâce à ce concept nous allons pouvoir passer directement à notre Func<DbDataReader,T>, un bloc de code qui sera interprété comme une fonction :

List<Personne> personnes = ConnectDb.Find(cmd, delegate(DbDataReader rs)
{
Personne p = new Personne();
p.mail = rs["mail"].ToString();
p.NomPrenom = rs["Nom"].ToString() + rs["prenom"].ToString();
return p;
});

    Le code est donc dérisoirement simple, on passe donc en paramètre au find notre delegate et directement à la ligne, grâce au délégué anonyme nous mettons notre bloc de traitement.

    Remarquez une chose assez intéressante, je n’ai pas spécifié le type générique utilisé pour la méthode Find. Alors que pourtant, le prototype de la fonction est bien Find<T>(…). En fait, le compilateur C# est “intelligent”, et déduit du code, le type générique utilisé et est capable de déterminer si il est correct ou non. Ce mécanisme s’appelle Inférence de Type.

   Rentrons maintenant dans une amélioration introduite par le framework 3.5, et que permet d’utiliser Func<Targ,TResult> : Les lambda Expressions. L’explication détaillée des lambda expressions n’entre pas dans le cadre de cet article, je me permet juste de vous montrer comment aurait pu s’écrire l’appel à la méthode Find en utilisant ce mécanisme, libre à vous d’approfondir vos recherches sur ce sujet :

List<Personne> personnesLambda = ConnectDb.Find(cmd, rs => new Personne()
{
mail = rs["mail"].ToString(),
NomPrenom = rs["Nom"].ToString() + rs["prenom"].ToString()
});

     Les lambda expressions reposent en grande partie sur l’Inférence de type dont je vous parlais plus haut.  Décomposons les grandes lignes de ce code :

    Tout d’abord le rs => new Personne(). J’ai volontairement conservé les nom des variables pour que l’exemple soit plus clair:

     Par inférence de type, le compilateur déduit que la variable rs doit être de type DbDataReader. la flèche (=>) introduit ensuite une ligne de code, devant nécessairement retourner dans notre cas, un objet de Type Personne (toujours par inférence de type). Je n’ai donc qu’à instancier mon objet personne et à initialiser ses membres publics.

      Ceci n’est qu’un exemple extrêmement simpliste de ce qu’il est possible de faire à l’aide des lambda Expressions, si vous souhaitez approfondir cette voie, je vous conseille d’effectuer des recherches sur LINQ introduit également au framework 3.5, au sujet duquel j’écrirais sans doute un article sous peu.

Conclusion

    Nous avons donc désormais entre les mains, une classe permettant d’abstraire tous les accès à une base de données, assez souple pour interagir avec n’importe quel provider .NET implémentant les classes du namespace System.Data.Common.

   Pour conclure, je joint à cet article le code source (librement utilisable, distribuable, commercialisable, etc) complet de cette classe avec les exemples que nous avons vu, et reste à votre entière disposition si vous avez des questions, ou simplement envie de débattre d’une autre méthode d’implémentation.

  Notez bien que tout le code que nous avons écrit ne manipule QUE les objets abstraits du namespace System.Data.Common. EN AUCUN CAS la classe ConnectDb ne doit contenir de référence à un provider spécifique. Nous perdrions d’emblée tout l’intérêt de l’utilisation d’une factory.

Enregistrer une ConnectionString dans un fichier de configuration

Pour stocker une chaîne de connexion à une base de donnée en dehors de l’application, la solution la plus simple est l’utilisation d’un fichier de configuration.

Ajouter un nouvel élément à votre projet, de type “Application configuration File”. Visual studio vous ajoute alors un fichier de configuration vide.

image

Entre les balises <configuration> et </configuration>, ajoutez un élément <connectionString> ajoutez un élément <add /> l’Intellisense vous propose une liste d’attributs, les plus important sont Name et ConnectionString . Voici un exemple pour une chaîne de connexion à SQL Server :

<add name="MyConnectionString"
connectionString="Data Source=MYSERVER\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;Pooling=False"
/>

Si vous ne savez pas quelle est la chaîne de connexion correspondant à votre base de donnée, consultez cet excellent site : http://www.connectionstrings.com/

Utilisation dans le code

Une fois votre fichier de configuration constitué, vous pouvez très simplement récupérer les valeurs saisies depuis votre code, à l’aide de la classe ConfigurationManager

Ajoutez une référence vers l’assembly System.configuration, vous pourrez ensuite récupérer la chaîne à l’aide de la ligne suivante :

String connectionString = ConfigurationManager.ConnectionStrings["MyDefaultConnectionString"].ConnectionString;

Et voilà, connectionString contient désormais la chaîne de connexion à votre base.

Note : Si votre solution comporte plusieurs projets, le fichier de configuration doit être placé dans le projet qui contient le point d’entrée de votre application.

Note 2 : Gardez bien à l’esprit que le fichier de configuration n’est pas chiffré par défaut ! Si vous décidez de stocker vos mots de passe en clair dans le fichier, ceux-ci pourront être visible à l’aide d’un simple éditeur de texte.

Abstraction des Accès BDD dans une Factory en ADO .NET

Dans le cadre du développement d’une application reposant sur l’utilisation d’une base de données, il est important de penser à la réutilisabilité de son code. Il n’est pas question de réécrire toute une application à chaque changement de moteur de BDD. Une des solution est donc de rajouter une couche intermédiaire entre la partie business et la base de donnée. Dans le pire des cas, seule cette couche est à réécrire.

En allant plus loin, il est possible d’intégrer les accès base dans une fabrique abstraite (factory). Microsoft nous a mâché le travail, depuis le framework .Net 2.0 le namespace System.Data.Common contient tout ce qu’il nous faut pour le faire facilement. Voici rapidement les types les plus important contenus dans ce namespace:

DbProviderFactories

Conteneur de Factory. C’est à travers ce type, que l’on va pouvoir binder notre architecture abstraite à un provider spécifique.

DbProviderFactory

Type implémenté pour chaque provider, c’est à travers un objet de ce type que l’on pourra instancier tous les types du provider choisi.

DbConnection

Type gérant la connexion à la base.

DbCommand

Type permettant d’envoyer des commandes (paramétrées ou nom) dans une DbConnection.

DbDataReader

Retour de l’exécution d’une DbCommand (un peu l’équivalent des bons vieux recordsets d’antan…)

Providers

Microsoft a déjà intégré un certain nombre de providers se conformant à cette architecture :
  • System.Data.SqlClient (SQL Server)
  • System.Data.OleDb (MS Access)
  • System.Data.OracleClient (Oracle DataBase)

D’autre providers sont disponibles sur internet, pour connaître ceux dont vous disposez sur votre machine vous pouvez soit jeter un oeil dans le fichier machine.config du framework à la section <DbProviderFactories>. Soit appeler la Méthode static  GetFactoryClasses() du type DbConnectionFactory :

DataTable tab =  DbProviderFactories.GetFactoryClasses();

foreach (DataRow row in tab.Rows)
{
foreach (DataColumn col in tab.Columns)
{
System.Console.WriteLine("{0} = {1} ;", col.ColumnName, row[col]);
}
Console.WriteLine(Environment.NewLine);
}

 

Mots clés Technorati : ,,,,