Comment transformer un fichier texte CSV en base de données(2).

21. March 2010 by Alexandre.FROMAGE

Précédemment, nous avons vu comment utiliser ADO.NET pour lire un fichier texte au format CSV. Dans cet article, nous allons aller plus loin : il s’agit de créer une classe générique permettant de manipuler des bases de données au format texte facilement. Nous l’appellerons « TextFileDataAccess ».

Les colonnes contenues dans le fichier texte seront associées à une propriété publique d’une classe.

Ainsi chaque fichier texte CSV aura une classe correspondante.

En nous basant sur le fichier csv de l’article précédent, nous obtenons donc la classe « User » :

 

 

    class User 
    { 
        
public Int32 IdUser { getset; } 
        
public string FirstName { getset; } 
        
public string LastName { getset; } 
        
public string Mail { getset; } 
        
public DateTime BirthDate { getset; } 
    } 

 

Pour rappel, le fichier CSV :

IdUser,FirstName,LastName,Mail,BirthDate 
0,John,Dubois,jdubois@alexandrefromage.fr,06/11/1989


L’interface ITextFileDataAccess

La classe« TextFileDataAccess » implémentera l’interface « ITextFileDataAccess ».Cette interface expose les quatre méthodes du CRUD :

    interface ITextFileDataAccess<T> where T : class 
    { 

        
//CRUD 
        
void Insert(T objet);   //Create 
        
List<T> List();         //Read 
        
void Delete(T objet);   //Update 
        
void Update(T objet);   //Delete 
    } 

Ici le type « T » correspondrait par exemple à la classe « User ».

La classe « TextFileDataAccess »

Voici le code de cette classe, seules « Insert» et « List » ont été implémentées: 

 

 

    class TextFileDataAcces<T> : ITextFileDataAccess<T> where T:class 
    { 
        
public TextFileDataAcces(string connectionString, string fileName) 
        { 
            
this.connectionString = connectionString; 

            
this.fileName = fileName; 

            
//HeadersEnable renvoie true si les headers sont utilisés 
            
this.headersEnable = this.HeadersEnable(); 
        } 

        
private List<T> ParseFile() 
        { 
            
//On initialise la liste 
            
List<T> list = new List<T>(); 

            
//On initialise la connection 
            
OleDbConnection connection = new OleDbConnection() 
            { 
                ConnectionString = 
this.connectionString 
            }; 
             

            
OleDbCommand command = new OleDbCommand(); 
            command.CommandType = System.Data.
CommandType.Text; 
            command.CommandText = 
"SELECT * FROM " + this.fileName; 
            command.Connection = connection; 

            
//Utilisation de la réflexion pour récupérer les propriétés publiques de la classe T 
            
Type type = typeof(T); 
            
PropertyInfo[] properties = type.GetProperties(); 


            
OleDbDataReader reader; 
            
int increment; 

            
using (connection) 
            { 
                connection.Open(); 

                
using (reader = command.ExecuteReader()) 
                { 
                    
while (reader.Read()) 
                    { 
                            
//On utilise la réflexion pour créer une instance d'objet de type T 
                            
//La classe T doit contenir un constructeur PUBLIC SANS PARAMETRES 
                            T objet = (T)
typeof(T).GetConstructor(Type.EmptyTypes).Invoke(null); 

                            
// 
                            increment = 
0

                            
//On utilise la réflexion pour parcourir toutes les propriétés publiques de T 
                            
foreach (PropertyInfo property in type.GetProperties()) 
                            { 

                                
//on utilise la réflexion pour accéder à une propriété de l'objet de type T 

                                
//Si la première du fichier contient les headers 
                                
//Chaque nom de propriété publique de l'objet de type T doit correspondre à 
                                
//un header --> IndexOutOfRangeException 
                                
if(this.headersEnable) 
                                    property.SetValue(objet, reader[property.Name], 
null); 
                                
//Si il n'y-a pas de headers 
                                
//Le nombre de propriétés publiques de l'objet de type T doit correspondre au 
                                
//nombre de colonnes --> IndexOutOfRangeException 
                                
else 
                                    property.SetValue(objet, reader[increment], 
null); 

                                increment++; 
                            } 

                            list.Add(objet); 

                    } 
                } 
            } 

            
return list; 
        } 

        
/// <summary> 
        
/// Analyse la chaine de connexion pour savoir si les headers sont utilisés. 
        
/// </summary> 
        
/// <returns>True si la première ligne du fichier texte contient les headers, False sinon</returns> 
        
private bool HeadersEnable() 
        { 
            
//On utilise le caractère ';' pour découper la chaine de connexion. 
            
//';' sépare tous les paramètres 
            
string[] connParams = this.connectionString.ToLower().Split(';'); 

            
foreach (string param in connParams) 
            { 
                
//Si le paramètre courant est HDR, on regarde sa valeur. 
                
if(param.Contains("hdr")) 
                    
return param.Contains("yes")== truetrue : false
            } 

            
//La propriété HDR n'a pas été trouvée. 
            
throw new Exception("HDR property not set!"); 
        } 

        
/// <summary> 
        
/// Parse le fichier et renvoie la liste d'objets correspondant. 
        
/// </summary> 
        
/// <returns>Liste d'objets de type T</returns> 
        
public List<T> List() 
        { 
            
//On parse le fichier et on renvoie la liste crée. 
            
return this.ParseFile(); 

        } 

        
/// <summary> 
        
/// Insère un objet de type T dans le fichier 
        
/// </summary> 
        
/// <typeparam name="T"></typeparam> 
        
/// <param name="objet"></param> 
        
public void Insert(T objet) 
        { 
            
//On génère la requete a exécuter. 
            
//Utilisation de la réflexion pour récupérer les propriétés publiques de la classe T 
            
Type type = typeof(T); 
            
PropertyInfo[] properties = type.GetProperties(); 

            
//On initialise la connection 
            
OleDbConnection connection = new OleDbConnection() 
            { 
                ConnectionString = 
this.connectionString 
            }; 

            
//Requête sql 
            
StringBuilder request = new StringBuilder("INSERT INTO " + this.fileName); 
            
StringBuilder nameRequest = new StringBuilder("("); 
            
StringBuilder valuesRequest = new StringBuilder(" VALUES("); 

            
//Commande 
            
OleDbCommand command = new OleDbCommand(); 

                
foreach (PropertyInfo property in properties) 
                { 
                    
//On crée un paramètres en utilisant: 
                    
//le nom de la propriété 
                    
//La valeur de la propriété dans l'objet à insérer 
                    
OleDbParameter dbParam = new OleDbParameter("@" + property.Name, property.GetValue(objet, null)); 
                    command.Parameters.Add(dbParam); 

                    valuesRequest.Append(
"@" + property.Name + ","); 

                    
//Si on utilise les headers, on utilise le nom des propriétés publiques pour 
            
//déduire les noms de colonne 
                    
if (this.headersEnable) 
                        nameRequest.Append(property.Name + 
","); 
                } 

            
//Noms 
                
if (this.headersEnable) 
                { 
                    
//On supprime la dernière virgule 
                    nameRequest.Remove(nameRequest.Length - 
11); 
                    
//On ajoute une ) 
                    nameRequest.Append(
")"); 
                    
//On concatène les éléments de la requête 
                    request.Append(nameRequest); 
                } 

            
//Valeurs 
            
//On supprime la dernière virgule 
            valuesRequest.Remove(valuesRequest.Length - 
11); 
            
//On ajoute une ) 
            valuesRequest.Append(
")"); 
            
//On concatène les éléments de la requête 
            request.Append(valuesRequest); 


            
// 
            command.CommandType = System.Data.
CommandType.Text; 
            command.CommandText = request.ToString(); 
            command.Connection = connection; 

            
using (connection) 
            { 
                connection.Open(); 
                
//On exécute la requête 
                command.ExecuteNonQuery(); 
            } 
        } 

        
public void Delete(T objet) 
        { 
            
throw new NotImplementedException(); 
        } 

        
public void Update(T objet) 
        { 
            
throw new NotImplementedException(); 
        } 



        
private string connectionString; 
        
private string fileName; 
        
private bool headersEnable; 
    } 

Cette classe supporte les fichiers texte utilisant et n’utilisant pas les headers. La chaine de connexion est analysée dans le constructeur pour détecter si les headers sont utilisés.

Exemple d’utilisation

Pour utiliser « TextFileDataAccess » :

 

            //Avec headers 
            
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + 
                
"C:/Users/Alexandre/Desktop/;Extended Properties=\"text;HDR=Yes;FMT=Delimited\";"
            
string fileName = "testHeaders.csv"

            
//Sans headers 
            
//string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + 
            
//    "C:/Users/Alexandre/Desktop/;Extended Properties=\"text;HDR=No;FMT=Delimited\";"; 
            
//string fileName = "testNoHeaders.csv"; 
             
            
TextFileDataAcces<User> daoUsers = new TextFileDataAcces<User>(connectionString, fileName); 


             
            
List<User> users = daoUsers.List(); 

            
User user = new User() 
            { 
                IdUser = 
51289
                FirstName = 
"Alexandre"
                LastName = 
"Fromage"
                Mail = 
"afromage@alexandrefromage.com"
                BirthDate = 
new DateTime(1989,11,06
            }; 

            daoUsers.Insert(user); 

 

Et voilà !

Vous pouvez maintenant utiliser très facilement des fichiers texte pour y stocker vos données.

Fichiers sources: ITextFileDataAccessTextFileDataAccess.

Article original.

ADO.NET, C# , , , ,

Comment transformer un fichier texte CSV en base de données(1)

21. March 2010 by Alexandre.FROMAGE

Récemment, j’ai du travailler sur une application qui devait traiter des données issues d’un fichier texte CSV (comma-separated values).

Pour des applications stockant peu de données et avec un modèle de données simple, l’utilisation d’une base de données relationnelle n’est souvent pas nécessaire.

Dans ce premier article, je vous expliquerai comment ADO.NET nous permet de communiquer avec un fichier texte, pour stocker des données.

Pour cela, il existe deux méthodes :

  • Lire le fichier texte à l’aide de StreamReader puis le parser.
  • Utiliser le fournisseur de données OLEDB avec ADO.NET.

Utiliser OLEDB simplifie grandement la manipulation des données stockées dans le fichier texte et nous permet d’être indépendants du format utilisé (SQL server, feuille excel, fichier texte…).

Dans cet article, nous allons voir comment utiliser un fichier texte CSV au travers d’OLEDB pour le stockage de nos données.

1. Le fichier CSV : Comma-Separated Values

En pratique, le séparateur peut être n’importe quel caractère.

Pour cet article, le fichier utilisé est le suivant :

IdUser,FirstName,LastName,Mail,BirthDate 0,John,Dubois,jdubois@alexandrefromage.fr,06/11/1989 1,Pierre,Duchamps,pduchamps@alexandrefromage.fr,07/12/1980 2,Paul,Dujardin,pdujardin@alexandrefromage.fr,23/01/2000 3,Henri,Dès,hdes@alexandrefromage.fr,15/02/1997 189,Alexandre,Dufort,adufort@alexandrefromage.fr,15/01/1960 12,Loïc,Dumaison,ldumaison@alexandrefromage.fr,19/06/1945


2. Lecture de données dans un fichier texte CSV avec ADO.NET

        //Connection string 
        
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + 
        
"C:/Users/Alexandre/Desktop/;Extended Properties=\"text;HDR=Yes;FMT=Delimited\";"

        
//Nom du fichier csv 
        
string fileName = "testHeaders.csv"

        
//On initialise la connection 
        OleDbConnection connection = 
new 
        OleDbConnection() 
        { 
            ConnectionString = connectionString 
        }; 

        
//On prépare la requête sql 
        OleDbCommand command = 
new 
        OleDbCommand() 
        { 
            CommandType = System.Data.CommandType.Text, 
            CommandText = 
"SELECT * FROM " + fileName, 
            Connection = connection 
        }; 

        
//Data reader 
        OleDbDataReader reader; 

        
//La connexion est automatiquement fermée à la fin du using 
        
using (connection) 
        { 
            
//Ouverture de la connexion 
            connection.Open(); 
            reader = command.ExecuteReader(); 

            
//On lit les enregistrements ligne par ligne 
            
while (reader.Read()) 
            { 
                
//Si on utilise la ligne de header 
                
Console.WriteLine(reader["IdUser"] + " " + reader["FirstName"]); 

                
//Sans header ? décommenter la ligne suivante 
                
//Console.WriteLine(reader[0] +  "  " + reader[1]); 

            } 
        } 

On obtient :

Ici, rien de compliqué. Le plus intéressant se concentre dans la chaine de connexion :

"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:/Users/Alexandre/Desktop/;Extended Properties=\"text;HDR=Yes;FMT=Delimited\";" 

Data Source: répertoire du fichier csv.

HDR indique si la première ligne du fichier CSV est le header(Yes) ou une ligne de données(No).

FMT indique comment les données sont séparées : « Delimited » si les colonnes sont séparées par une virgule ou « Fixed » si chaque colonne a une dimension. Dans ce dernier cas, il faut utiliser « Schema.ini » pour spécifier la taille des colonnes.

Schema.ini vous permet de spécifier :

-le nom du fichier texte

-le séparateur utilisé (format)

-les noms, tailles et longueur des champs des colonnes

Plus d’informations ici.

Dans le prochain article, nous créerons une classe générique, utilisant la réflexion, qui nous simplifiera la manipulation de fichiers CSV.

ADO.NET, C# , ,