Barre horizontale de naviagtion

mardi 22 décembre 2009

report

classe DTO

[Serializable]
public class AnalyseAgeDTO
{
#region Constructeur
public AnalyseAgeDTO()
{
}
#endregion
#region property
string _TrancheAge;
public string TrancheAge
{
get { return _TrancheAge; }
set { _TrancheAge = value; }
}
int _Effectif;
public int Effectif
{
get { return _Effectif; }
set { _Effectif = value; }
}
double _PourcentageMarche;
public double PourcentageMarche
{
get { return _PourcentageMarche; }
set { _PourcentageMarche = value; }
}
double _MedianeSalaireMarche;
public double MedianeSalaireMarche
{
get { return _MedianeSalaireMarche; }
set { _MedianeSalaireMarche = value; }
}
double _MedianeSalaireSociete;
public double MedianeSalaireSociete
{
get { return _MedianeSalaireSociete; }
set { _MedianeSalaireSociete = value; }
}
#endregion
}


classe DAO

[Serializable ]
public class AnalyseAgeFromRdb
{
#region Constructeur
public AnalyseAgeFromRdb()
{
}
#endregion
#region Dataset(list seralizable)
public List AnalyseParAge = new List();
public List getAnalyseParAge(int Annee, long? MarcheID, long? ClientID, int TypeStatutID ,int TypeSalaireID)
{
string psName = "getAnalyseParAgeParTranche";
SqlDataReader sqlReader = null;
SqlConnection dbConnexion = null;
List lst = new List();
try
{
dbConnexion = ConnectToDB.getConnection();
SqlCommand cmd = new SqlCommand(psName, dbConnexion);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@Annee", Annee);
cmd.Parameters.AddWithValue("@MarcheID", MarcheID);
cmd.Parameters.AddWithValue("@ClientID", ClientID);
cmd.Parameters.AddWithValue("@TypeStatutID", TypeStatutID);
cmd.Parameters.AddWithValue("@TypeSalaireID", TypeSalaireID);
sqlReader = cmd.ExecuteReader();
while (sqlReader.Read())
{
AnalyseAgeDTO item = new AnalyseAgeDTO();
item.Effectif = int.Parse(sqlReader["EffectifSociete"].ToString());
item.MedianeSalaireMarche = double.Parse(sqlReader["MedianeMarche"].ToString());
item.MedianeSalaireSociete = double.Parse(sqlReader["MedianeClient"].ToString());
item.TrancheAge = sqlReader["TrancheAge"].ToString();
item.PourcentageMarche = double.Parse(sqlReader["pourcentParRapportMarche"].ToString());
lst.Add(item);
}
}
catch (Exception exp)
{
dbConnexion.Close();
}
return lst;
}
#endregion
}

mercredi 9 décembre 2009

[.NET] Lire un fichier Excel avec le SDK Open XML 2.0

Pour les besoins d’un projet sur lequel je devais lier un fichier Excel à un DataGrid WPF 4.0, je me suis intéressé à la lecture de ce type de fichier à l’aide du SDK Open XML 2.0 que vous pouvez télécharger sur cette page.

Après plusieurs heures de recherches, j’ai enfin réussi à lire mon fichier :) Dans ce post, je vais vous expliquer la marche à suivre.

Partons d’un cas simple où le fichier Excel contient une liste de client de votre entreprise :

Excel-OpenXML-01

Chaque client sera représenté par une instance de la classe “Customer” ci-dessous :

  1. public class Customer
  2. {
  3. public string ID { get; set; }
  4. public string LastName { get; set; }
  5. public string FirstName { get; set; }
  6. public string City { get; set; }
  7. }

Si vous aviez lu mon article traitant de la génération de document Word OpenXML, vous vous souvenez certainement que la particularité d’un document suivant cette norme OpenXML est de n’être rien d’autre qu’une archive zip contenant différents fichiers XML décrivant la structure et le contenu du document :

Excel-OpenXML-03

Sur l’image ci-contre, vous pouvez voir les différentes parties du fichier Excel qui nous intéressent ici :

- le dossier “worksheets” contient toutes les feuilles qui composent le document.

- le fichier “sharedStrings.xml” contient toutes les valeurs texte que nous avons entrées dans chacunes des cellules du document.

- le fichier “workbook.xml” représente le classeur Excel à proprement parlé.

Après avoir installé le SDK Open XML 2.0 (April 2009 CTP) dont le lien est indiqué ci-dessus, ajoutez une référence vers la librairie “DocumentFormat.OpenXML” :

Excel-OpenXML-02

C’est dans cette dernière que vous aller trouver les classes permettant de lire le fichier Excel, à savoir :

  • SpreadsheetDocument : cette classe représente le document Excel à proprement parlé.
  • WorkbookPart : c’est la classe qui correspond au fichier “workbook.xml” vu ci-dessus.
  • WorksheetPart : cette classe permet de parser chaque fichier XML associé à une feuille de calcul Excel (dans le dossier “worksheets” de l’archive).
  • SharedStringTablePart : cette classe représente le fichier “sharedStrings.xml
  • SheetData : cette classe va permettre la récupération des lignes (et donc des cellules d’une feuille de calcul).
  • Row : représente une ligne dans un classeur
  • Cell : représente une cellule dans une ligne. Chaque cellule est identifiée par un numéro unique, permettant ainsi de mapper la valeur dans le fichier “sharedStrings.xml

Maintenant que les grands principes sont posés, nous pouvons rentrer plus en détails dans le code de l’application.

La classe SpreadsheetDocument possède une méthode statique “Open” permettant d’ouvrir un fichier Excel 2007/2010 (.xlsx) à partir de son chemin d’accès :

  1. string customersFilePath = "Customers.xlsx";

  2. //ouverture du fichier en lecture seule
  3. using (SpreadsheetDocument customersDocument
  4. = SpreadsheetDocument.Open(customersFilePath, false))
  5. {

  6. }

Nous pouvons alors récupérer le “WorkbookPart” ainsi que l’instance de “WorksheetPart” associée à la première feuille de calcul du fichier Excel :

  1. WorkbookPart workbookPart = customersDocument.WorkbookPart;
  2. WorksheetPart worksheetPart = workbookPart.GetPartsOfType().First();

Le WorkbookPart permet également la récupération de la classe qui va permettre la lecture des valeurs des cellules dans le fichier “sharedStrings.xml” :

  1. SharedStringTablePart sharedStringTablePart =
  2. workbookPart.GetPartsOfType().First();

Nous avons tout pour commencer à lire le fichier de clients. Pour cela, nous devons récupérer l’instance de “SheetData” associée à la feuille de calcul courante :

  1. SheetData sheetData = worksheetPart.Worksheet.Descendants().First();

C’est sur celle-ci que nous allons récupérer lignes et colonnes :

  1. //parcours des lignes sauf la première (titre de colonne)
  2. foreach (var row in sheetData.Elements().Skip(1))
  3. {
  4. var cells = row.Elements().ToList();
  5. }

Il faut ensuite s’assurer que nous avons bien récupérer les 4 colonnes de notre document. Après cela, nous pouvons récupérer l’identifier unique de la colonne pour aller chercher sa valeur dans la “SharedStringTablePart” :

  1. if (cells.Count == 4)
  2. {
  3. Customer cust = new Customer();

  4. //ID
  5. Cell IDCell = cells[0];
  6. //si le type de donnée est bien un SharedString
  7. if (IDCell.DataType == CellValues.SharedString)
  8. {
  9. //on récupère l'id de la cellule
  10. int cellNumber = int.Parse(IDCell.CellValue.InnerText);
  11. //on récupère la valeur dans la shared table
  12. cust.ID = sharedStringTablePart.SharedStringTable.ChildElements[cellNumber].InnerText;
  13. }
  14. }

Note : pour ne pas surcharger la lecture, j’ai volontairement omis les autres propriétés de notre Client récupérable de manière identique dans les cellules d’index 1,2 et 3 :)

Vous avez donc récupéré votre liste de client en mémoire, il suffit de la lier à un DataGrid :








Et voilà le travail :

Excel-OpenXML-04

A vous de jouer !

A bientôt Wink

Sources : ExcelOpenXmlSDK.zip (68.05 kb)

Introduction à Windows Communication Foundation

Ce billet est le dernier de ma série d’articles d’introduction à Windows Communication Foundation. Vous pouvez lire les 4 précédents via cette page.

Ici, nous allons nous intéresser à un point que je n’ai volontairement pas abordé dans les articles précédents : la gestion des exceptions et des fautes SOAP.

Commençons par un cas simple, où le service va simplement définir une opération levant une exception.

Cas simple

Nous allons créer un service ExceptionService. Il mettra en jeu les différentes entités fondamentales de WCF, à savoir un contrat de service, une implémentation, une hôte et un client. Les deux dernières applications seront des applications console classique utilisant respectivement les classes ServiceHost et ChannelFactory.

Le contrat de service est lui très simple, puisqu’il ne définit qu’une seule méthode : ThrowException :

wcfExceptions2

L’implémentation du service quant à elle redéfinie bien évidemment la méthode ThrowException est lève tout simplement une Exception:

wcfExceptions3

Vous pourrez retrouver tout le code de cette mise en situation au base de cet article.

Notre client est très simple puisqu’il appel tout simplement la méthode ThrowException du service, au travers de l’instance de ChannelFactory.

wcfExceptions4

Le service est exposé sur un endpoint ayant un binding de type BasicHttpBinding. Voyons le résultat d’exécution du client :

wcfExceptions5

Vous pouvez constater ici que bien que ce soit une exception de type Exception qui soit levée dans la méthode ThrowException du service, notre client intercepte ici un autre type d’exception : FaultException. Quoi qu’il en soit, nous voyons qu’il y a bien une erreur côté serveur, mais nous n’avons aucun détail quant la nature de celle-ci.

Si nous plaçons un espion au niveau de l’exception levée dans le client WCF, nous pouvons voir qu’il ne s’agit bien évidemment pas d’une simple Exception mais d’une FaultException :

wcfExceptions6

Définie dans l’espace de noms System.ServiceModel, FaultException est une exception permettant de représenter une faute SOAP. En effet, SOAP, le dérivée du XML utilisé pour transporter nos messages Windows Communication Foundation ne fonctionne pas comme un langage .NET avec un système d’exception. Voilà à quoi ressemble une faute SOAP :







Par défaut, le détail de la faute SOAP n’est pas en relation avec l’exception qui a réellement été levée et ce pour des raison de sécurité. Comme l’exception est envoyée côté client, il sera dangereux de donner trop de détails !

Cependant, en debug notamment, il peut être utile de connaître la nature de l’erreur qui s’est produit sur le serveur. Pour obtenir ce résultat, nous allons devoir redéfinir le comportement par défaut de notre service et lui ajouter une behavior de type serviceDebug afin d’inclure le détail dans la faute SOAP :

wcfExceptions7

Dès lors, vous aurez accès aux détails de l’exception qui a été levée côté serveur, au niveau du client, comme le montre la capture ci-dessous :

wcfExceptions8

Personnalisation des FaultException

Dans le cadre d’un projet, il est souvent utile de donner des informations aux clients de notre service, sans pour autant dévoiler tout le contenu de l’exception où même toutes les exceptions. Pour cela, le .NET Framework offre une version générique de la classe FaultException, elle aussi définie dans l’espace de noms System.ServiceModel.

Le type T de la classe FaultException est une classe sérialisable (un contrat de données) permettant d’obtenir plus d’informations sur l’erreur.

Ici, nous créeons un classe CustomFault ayant une propriété Reason représentant le détail de l’erreur à afficher :

wcfExceptions9

Nous allons créer une nouvelle opération à notre service WCF, celle-ci sera chargée de lever une exception de type FaultException. Nous choisissons d’appeler cette méthode ThrowFaultException :

wcfExceptions10

En voici l’implémentation :

wcfExceptions11

Il reste une petite modification à apporter à notre contrat de service afin que l’exception de type FaultException puisse se propager via SOAP jusqu’au client : l’ajout de l’attribut FaultContractAttribute au dessus de la définition de l’opération ThrowFaultException. Défini dans l’espace de nom System.ServiceModel, cet attribut va nous permettre de spécifier au moteur WCF le type T des FaultException levées au sein de l’opération :

wcfExceptions12

Dès lors, nous pouvons modifier le code de notre client afin qu’il affiche l’information dans le cas où une FaultException est levée sur le service WCF :

wcfExceptions13

Et voici le résultat en exécution, nous obtenons bien le résultat escompté, à savoir une exception gérée par notre service WCF puisque ne dévoilant que les informations nécéssaires :

wcfExceptions14

Je tiens à préciser ici que la directive qui nous permettait de propager le détail des exceptions en debug est cette fois ci évaluée à false et ce pour la sécurité d’exécution de notre service : seules les erreurs que nous souhaitons propager de manière détaillées le seront !

wcfExceptions15

J’espère que ce billet vous aura permis de comprendre les bases de la gestion des exceptions en WCF et surtout comment faire transiter celles-ci via SOAP, base des échanges entre client et service WCF, sans trop exposer notre service à des vulnérabilités en faisant le choix de tout envoyer !

A bientôt Wink