Filière : Techniques de Développement Informatiques
Module : Programmation site Web Dynamiques
Pour augmenter sa productivité au quotidien, la société Alpha a donc décidé de mettre en place une nouvelle infrastructure technique :
• mise en place d’une base de données centralisée ;
• accès sécurisé à cette base pour toutes les unités via une interface Web permettant d’effectuer le suivi des stocks et des commandes fournisseur, ainsi que l’analyse des ventes ;
• mise à jour automatique et quotidienne de la base, à partir d’informations extraites des fichiers de gestion commerciale utilisés à l’usine ;
• nouveau mécanisme d’échange avec les fournisseurs, fondé sur XML.
Dans les sections qui suivent, nous détaillons les différentes composantes du projet :
• structure de la base de données ;
• interface de gestion de la base ;
• transferts de données automatisés (échanges fournisseur, mise à jour des données depuis l’usine).
L’intérêt de mettre en place une base centrale est de disposer d’un référentiel de Données unique partagé entre toutes les unités , on évitera ainsi les pertes du temps dues à la mise à jour manuelle des données de suivi des stocks et d’analyse des ventes, et on donnera à tous l’accès à une information à jour. Cette base accessible via Internet, sera hébergée chez un prestataire externe. Les données à stocker dans la base peuvent être réparties en deux sous ensembles :
• données utilisées pour l’analyse des ventes et le suivi des stocks ;
• données utilisées pour la gestion des commandes fournisseur.
Pour éviter une trop grande complexité, on se limitera aux hypothèses suivantes :
• le département marketing souhaite pouvoir consulter la répartition des ventes par famille de produits ou par région, pour un mois donné.
• le département logistique souhaite pouvoir suivre l’évolution des stocks de Produits finis.
Pour effectuer ces opérations, il faut au minimum stocker dans la base :
• la liste des produits ;
• la liste des familles de produits ;
Base de données utilisé pour le cours :
Pour assurer le suivi des commandes fournisseur, le département logistique souhaite pouvoir garder trace des références fournisseur commandées (type et quantité), de la date de livraison prévue et du statut de la livraison (commande livrée ou non). Par conséquent, il faut stocker dans la base :
• la liste des fournisseurs ;
• la liste des références fournisseur (produits proposés par ces fournisseurs) ;
• la liste des commandes passées auprès de ces fournisseurs (avec les lignes de commandes associées).
Données nécessaires au suivi des commandes fournisseur
Entité Caractéristiques, relations
Fournisseur | Code fournisseur, nom et adresse À un fournisseur sont associées de 1 à n références fournisseur |
Commande | • Date création, date livraison prévue, date livraison effective • Une commande est rattachée à un fournisseur • 1 à n lignes de commandes lui sont associées |
Ligne de commande | Référence fournisseur, quantité commandée, quantité livrée • Rattachée à une commande |
Référence fournisseur | Référence et désignation du produit • Associée à un fournisseur |
Dans ce chapitre, nous réalisons l’interface de suivi des stocks, qui repose principalement sur l’utilisation de contrôles serveur (DataGrid et DropDownList) liés à la source de données par l’intermédiaire de la bibliothèque , que nous présentons au passage. Nous abordons également le mécanisme des événements serveur, lequel nous permettra d’ajouter de l’interactivité à notre interface.
Le module de suivi des stocks doit permettre, d’une part, l’affichage de la synthèse des quantités en stock, filtrées par famille de produits et, d’autre part, la consultation de l’historique des variations de stocks pour un produit donné.
Notre interface sera, par conséquent, constituée de deux pages distinctes :
• La page , écran d’accueil du module, présentera la liste des stocks disponibles pour une famille de produits donnée. Il devra être possible de sélectionner la famille désirée dans une liste déroulante, la liste étant alors mise à jour en conséquence , et il devra également être possible de cliquer sur un produit donné pour accéder à l’historique du stock de ce produit.
• La page présentera l’historique des stocks d’un produit donné (détail des ventes et approvisionnement par mois).
Comme pour toute page , le développement se déroulera en deux temps :
• réalisation de la maquette de la page ;
• implémentation de la cinématique de la page.
Nous allons donc commencer par réaliser la maquette de ces deux pages.
À la fin du chapitre précédent, nous avons réalisé le squelette de la page d’accueil du module de suivi des stocks (fichier ), dans laquelle nous avons déjà intégré la barre de navigation. Nous allons repartir de cette ébauche, à laquelle nous allons ajouter deux contrôles serveur :
• un contrôle de type DropDownList (liste déroulante) pour afficher la liste des familles ; • un contrôle de type DataGrid (grille de données) pour afficher l’état des stocks.
Le résultat auquel nous parviendrons lorsque la page sera réalisée est représenté Avant de modifier notre page, nous allons en sauvegarder une copie, qui nous servira également de point de départpour la page de consultation détaillée de l’historique :
1. Ouvrez la version actuelle du fichier . 2. Sauvegardez-la sous le nom , dans le même répertoire.
Ceci étant fait, nous pouvons passer à la réalisation de la maquette de notre première page :
1 Ouvrez à nouveau la version actuelle du fichier .
Figure :La page de consultation des stocks par famille de produits
Insérez un titre (par exemple : « Consultation de l’état du stock par famille de produits »).
1. Créez un tableau HTML (une ligne ; deux colonnes, arrière-plan jaune).
2. Insérez un contrôle serveur DropDownList dans la partie droite de ce tableau et renommez-le ListeFamilles.
3. Insérez un contrôle serveur DataGrid sous le tableau et renommez-le EtatStock. Le résultat de la maquette doit ressembler à celle montrée figure ci dessous.
Figure :Maquette de la page de suivi des stocks
Voici le code de la partie graphique du fichier après ajout de quelques attributs de style pour contrôler la largeur des éléments (les contrôles serveur sont indiqués en couleurs) :
(partie graphique) Suivi des stocks Consultation de l'état du stock par famille de produits | Famille | |
Nous avons fait la moitié du travail de réalisation de la maquette ; passons maintenant à la page de consultation de l’historique du stock d’un produit donné.
Pour réaliser cette page, nous allons partir de l’ébauche de page, enregistrée au paragraphe précédent sous le nom , dans laquelle nous allons ajouter :
• un contrôle de type Label (étiquette de texte) pour afficher le nom du produit ; • un contrôle de type DataGrid (grille de données) pour afficher l’historique du stock. Le résultat auquel nous parviendrons lorsque la page sera réalisée est représenté figure ci dessous.
La page de consultation de l’historique du stock pour un produit
Voici les étapes nécessaires à la réalisation de cette page :
1 Ouvrez le fichier créé précédemment.
2 Placez-vous dans l’onglet Design.
3 Insérez un contrôle serveur de type Label, renommez-le NomProduit, puis appliquez- lui le style Heading 4 et faites-le suivre du texte : « historique du stock ».
4 Insérez un contrôle serveur de type DataGrid et renommez-le HistoriqueStock.
5 Insérez un lien hypertexte « Retour à la page d’accueil du module » pointant vers la page .
Le résultat de la maquette doit ressembler à l’illustration de la figure 4-4. Voici le code de la partie graphique du fichier :
Suivi des stocks
Retour à la page d'accueil du module
Nous en avons terminé avec la phase de maquettage… passons maintenant au cœur du problème : l’implémentation du lien entre l’interface et la base de données.
À l’image d’un grand nombre de contrôles serveur , DropDownList et DataGrid offrent la possibilité d’être liés à une source de données : autrement dit, leur contenu peut être automatiquement mis à jour à partir de valeurs contenues dans une base de données, un fichier XML, voire un tableau en mémoire.
Nous allons utiliser cette fonctionnalité pour :
• afficher la liste des familles dans le contrôle ListeFamilles à partir des valeurs contenues dans la table FamilleProduit ;
• afficher l’état du stock correspondant dans le contrôle EtatStock à partir du résultat de l’exécution de la procédure EtatStock.
La communication avec la source de données s’effectuera par l’intermédiaire de la bibliothèque , dont nous allons présenter les principales classes.
est un nom commercial qui désigne un ensemble de classes utilisées pour communiquer avec des sources de données et manipuler des données
• L’espace de nommage fournit des classes utilitaires qui permettent de manipuler des données ; ces classes sont toutes indépendantes de la source de données.
• L’espace de nommage .OleDb fournit les classes nécessaires à la communication avec une source de données OLE-DB.
• L’espace de nommage .SqlClient fournit les classes nécessaires à la communication avec une base de données SQL Server ou MSDE.
Schéma :
La première caractéristique notable de l’architecture de cette bibliothèque est la séparation nette entre les classes de communication, dépendantes de la source de données, et les classes de manipulation, indépendantes de la source de données : le but étant de disposer d’une séparation entre la couche présentation et la couche accès aux données, afin de rendre le code plus modulaire et plus facile à faire évoluer.
Le second point notable est la richesse de la bibliothèque , qui dispose de toutes les classes nécessaires à la création d’une véritable petite base de données en mémoire, incluant un schéma relationnel, des contraintes et même des déclencheurs ! L’intérêt principal d’une telle architecture est d’adapter l’accès aux données au caractère déconnecté des applications Web : les classes de manipulation permettent en effet de conserver les données en cache et de transmettre les changements effectués à la base, lors de la connexion suivante (nous aurons l’occasion d’illustrer ce mécanisme dans le chapitre suivant).
À l’heure actuelle, propose deux modes d’accès aux données :
• Utilisation du fournisseur OLE-DB, qui permet d’accéder à toute base de données dotée d’un pilote OLE-DB (classes correspondantes implémentées dans l’espace de nommage .OleDb).
• Utilisation du fournisseur SQL natif, qui permet d’accéder de manière native (autrement dit, plus rapidement et avec accès à l’ensemble des fonctionnalités disponibles) aux bases de données SQL Server ou MSDE (classes correspondantes implémentées dans l’espace de nommage .SqlClient).
Si vous disposez d’une base de données SQL Server ou MSDE, il est fortement recommandé d’utiliser le fournisseur SQL natif, plus performant : c’est ce que nous allons faire dans la section suivante, en implémentant la connexion à la base avec SqlConnection.
Dans cette section, nous allons voir comment utiliser la bibliothèque pour se connecter à une base de données, à l’aide de la classe SqlConnection.
Notre connexion étant destinée à être utilisée plusieurs fois au sein de notre application, nous allons également voir comment la partager, autrement dit, l’implémenter dans un endroit central accessible à tous les éléments de l’application, grâce à l’objet Session et au fichier .
La connexion à la base est implémentée par une instance SqlConnection dont le paramétrage s’effectue par l’intermédiaire d’une chaîne de connexion de la forme :
SqlConnection myConnection = new SqlConnection(); myConnection.ConnectionString="" ();
// Effectuer ici des opérations sur la base myConnection.Close();
Où allons-nous placer ce code de connexion/déconnexion à la base de données ?
Une première option est de le répéter au sein de chaque page : cette solution n’est pas optimale puisqu’elle alourdit le code de l’application, dégrade la performance (nombreuses opérations de connexion/déconnexion) et rend la maintenance difficile en cas de modification des options de connexion.
Une seconde option, préférable, consiste à se connecter au début de la session de l’utilisateur et à conserver la connexion dans un endroit central, accessible à tous les éléments de l’application : c’est cette technique que nous allons exposer dans la section suivante.
Deux éléments vont nous permettre d’implémenter le partage de la connexion à la base de données :
• l’objet Session, qui permet de partager des données entre tous les éléments d’une application Web pendant la durée d’une session utilisateur (une instance différente est créée pour chaque session utilisateur) ;
le fichier , qui permet de stocker des variables globales et d’implémenter des gestionnaires devant être exécutés au début et à la fin d’une session utilisateur, ainsi qu’au démarrage et à l’arrêt d’une application Web.
Voici le processus que nous souhaitons implémenter :
1 Ouverture d’une connexion au démarrage de chaque session utilisateur.
2 Stockage de cette connexion dans le dictionnaire de l’objet Session correspondant.
3 Fermeture de la connexion lors de la fin de la session.
Le code correspondant, à implémenter dans le fichier (lequel aura
SqlConnection, au lieu de . SqlClient.SqlConnection. Sub Session_Start(Sender As Object,E As EventArgs ) Dim myConnection As SqlConnection myConnection = new SqlConnection() myConnection.ConnectionString="” () Session("myConnection") = myConnection End Sub |
été préalablement créé et placé dans le répertoire racine de l’application), est présenté et commenté ci-après.
Dans ce gestionnaire exécuté au début de chaque session, on crée et on ouvre la connexion à la base, puis on stocke l’objet correspondant dans le dictionnaire de l’objet Session (remplacer
par votre véritable chaîne de connexion).
Sub Session_End(Sender As Object,E As EventArgs ) Dim myConnection As SqlConnection myConnection = CType(Session("myConnection"),SqlConnection) myConnection.Close() End Sub |
(Version C#)
<%@Import Namespace = ".SqlClient"%>
Comme nous l’avons indiqué plus haut, le contrôle serveur DropDownList offre la possibilité d’être automatiquement rempli à partir de valeurs contenues dans une base de données. En pratique, ce lien s’effectue à l’aide de trois propriétés du contrôle :
• la propriété DataSource, utilisée pour spécifier la source de données (dans notre cas, la table FamilleProduit) ;
• la propriété DataTextField, utilisée pour spécifier le nom du champ à afficher dans la liste (dans notre cas, le champ NomFamille) ;
• la propriété DataValueField, utilisée pour spécifier la valeur à associer aux éléments de la liste (dans notre cas, le champ ID_FamilleProduit).
Pour établir le lien entre la table de données et la liste déroulante, deux options sont possibles :
• utiliser des classes SqlCommand et SqlDataReader ; • utiliser des classes SqlDataAdapter, DataTable et DataView.
L’option avec utilisation de SqlDataReader est, a priori, la plus simple et la plus performante ; quant à la seconde option, elle est fréquemment présentée dans la documentation .NET et permet de présenter des classes importantes comme SqlDataAdapter et DataTable.
Nous allons donc implémenter deux versions d’une fonction permettant d’effectuer le chargement de la liste des familles, que nous nommerons ChargerListeFamilles. Puis, nous intégrerons cette fonction dans notre page .
Cette première option utilise trois classes extraites de l’espace de nommage .SqlClient :
• La classe SqlConnection, déjà décrite précédemment, implémente la connexion à la base de données.
• La classe SqlCommand permet de spécifier à quel objet de la base de données on s’intéresse, en l’occurrence la table FamilleProduit.
• La classe SqlDataReader permet de manipuler les données, en l’occurrence les parcourir en lecture seule.
Soit la page
– Fonction ChargerListeFamilles (version C#/SqlDataReader)
void ChargerFamillesProduits() { SqlCommand myCommand; SqlDataReader myReader; SqlConnection myConnection = (SqlConnection)Session["myConnection"]; string SQL = "SELECT * FROM FamilleProduit"; myCommand = new SqlCommand(SQL,myConnection); myReader = myCommand.ExecuteReader(); ListeFamilles.DataSource = myReader; ListeFamilles.DataValueField = "ID_FamilleProduit"; ListeFamilles.DataTextField = "NomFamille"; ListeFamilles.DataBind(); myReader.Close();} |
Cette deuxième option utilise quatre classes :
• La classe SqlConnection, déjà décrite précédemment, implémente la connexion à la base de données.
• La classe SqlDataAdapter permet de gérer la communication avec un objet de la base de données de manière bidirectionnnelle (lecture, mise à jour, suppression de données).
• La classe DataTable représente une table en mémoire dont il est possible de manipuler les données, en lecture et en écriture.
• La classe DataView représente une vue d’une table, autrement dit, un sousensemble énumérable des lignes de la table.
Les deux premières classes font partie de l’espace de nommage
.SqlClient (elles sont donc dépendantes de la base de données) tandis que les deux dernières sont extraites de (elles sont donc indépendantes de la base de données).
Comme on le voit sur la figure ci dessur , cette solution constitue en quelque sorte « la grosse artillerie » pour remplir une liste déroulante :
• Une copie de la table FamilleProduit est conservée en mémoire (gérée par DataTable), ce qui consomme inutilement des ressources.
• La classe SqlDataAdapter est utilisée pour remplir cette table en mémoire ; néanmoins, dans notre cas, elle est sous-utilisée car elle pourrait également servir à synchroniser les changements effectués sur la version « en mémoire » vers la base de données.
L’intérêt de présenter cette solution est donc plus didactique que technique !
Voici la version correspondante de la fonction – Fonction ChargerListeFamilles (version C#/SqlDataAdapter)
void ChargerListeFamilles() { SqlConnection myConnection = (SqlConnection)Session["myConnection"]; SqlDataAdapter myAdapter; DataTable myDataTable; string strSQL = "SELECT * FROM FamilleProduit"; myAdapter = new SqlDataAdapter(strSQL,myConnection); myDataTable = new DataTable(); (myDataTable); ListeFamilles.DataSource = myDataTable.DefaultView; ListeFamilles.DataValueField = "ID_FamilleProduit"; ListeFamilles.DataTextField = "NomFamille"; ListeFamilles.DataBind(); } |
Voilà ! Si tout se passe bien, vous devez maintenant voir apparaître la liste des familles de produits remplie lorsque vous exécutez la page, Nous avons réalisé la première moitié de la page. Passons maintenant à la seconde : l’implémentation du contrôle DataGrid destiné à afficher l’état des stocks.
La liaison entre le contrôle EtatStock de type DataGrid et la procédure stockée EtatStock va s’effectuer de manière similaire à ce que nous avons réalisé dans la section précédente. Rappelons que deux techniques sont à notre disposition :
• utiliser SqlCommand et SqlDataReader ;
• utiliser SqlDataAdapter, DataTable et DataView.
Nous allons ici opter pour l’utilisation de SqlDataReader, la technique la plus performante et la mieux adaptée à nos besoins. Encore une fois, nous allons implémenter le paramétrage dans une fonction distincte, que nous nommerons AfficherStockProduits, profitant ainsi des possibilités de structuration du code offertes par .
La seule particularité supplémentaire est ici l’emploi d’une procédure stockée (ce que nous spécifions grâce à la propriété CommandType de SqlCommand) qui accepte en entrée un paramètre de type entier égal au numéro de la famille sélectionnée dans ListeFamilles.
– Fonction AfficherStocksProduits (version C#)
void remplirGrid() { string sqlstr="" ; sqlstr = "SELECT ID_Produit , Designation, ID_FamilleProduit, Code FROM dbo.Produit"; sqlstr+=" WHERE (ID_FamilleProduit = @ID_FamilleProduit)"; SqlConnection cn = new SqlConnection("votre chaine de connexion"); (); int ID_Famille = int.Parse(ListeFamilles.SelectedValue); SqlCommand cmd = new SqlCommand(sqlstr, cn); cmd.Parameters.AddWithValue("@ID_FamilleProduit", ID_Famille); SqlDataReader dr = cmd.ExecuteReader(); |
EtatStock.DataSource = dr; EtatStock.DataBind(); dr.Close(); } |
Pour l’appel sur page load :
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ChargerListeFamilles1(); } remplirGrid(); } |
Remarque importante :
Ajouter une colonne pour afficher la page : qui contient les Mouvements Stock du produit sélectionné
Pour ce faire on doit ajouter a notre gridView1 une colonne de type :HyperLinKField
Démarche a suivre :
Puis on renseigne les informations suivantes :
Puis ok
Sur le navigateur :
Quand on passe le curseur de souris sur le lien
En bas de la page s’affiche la page a laquelle va nous rediriger le lien
Les informations utilisées pour créer cet URL dynamique sont:
Code de création :
DataNavigateUrlFormatString="?ID_Produit={0}"
DataTextField="ID_Produit"
DataTextFormatString="details du produit: {0}"
HeaderText="selectionnez" />
Par défaut, les colonnes du DataGrid sont générées dynamiquement à partir des colonnes présentes dans la source de données. Dans notre cas, nous souhaitons n’afficher que les colonnes Code, Désignation et Stocks, à l’exclusion des colonnes clés (ID_Produit et ID_FamilleProduit). En outre, les éléments de la colonne Désignation doivent agir comme des liens hypertextes qui permettent d’accéder à l’historique du stock. Pour cela, nous allons désactiver la fonctionnalité de génération automatique des colonnes (propriété AutoGenerateColumns) et spécifier une liste personnalisée de colonnes :
• colonne Code de type BoundColumn ;
• colonne Désignation de type HyperLinkColumn ;
• colonne Stocks de type BoundColumn.
Voici la marche à suivre pour spécifier notre propre liste de colonnes :
Les types de colonnes gérés par DataGrid
Une grille de données (contrôle DataGrid) peut contenir des colonnes de type
BoundColumn (colonne simple liée à un champ de la base),
HyperLinkColumn (colonne contenant des liens hypertextes), ButtonColumn (colonne contenant des boutons d’actions), TemplateColumn (colonne dont le format est fixé par l’utilisateur) et EditCommandColumn (colonne permettant d’éditer les valeurs de la ligne).
1. Faites apparaître la boîte de dialogue de paramétrage de EtatStock (voir précédemment).
2. Cliquez sur Columns afin de faire apparaître la feuille de propriétés relative aux colonnes.
3. Désactivez l’option Create columns automatically at run time.
4. Créez une nouvelle colonne de type BoundColumn ayant les caractéristiques suivantes :
Header Text | Code | Le texte de l’en-tête de la colonne |
Data Field | Code | Le champ correspondant de la base |
Créez une nouvelle colonne de type HyperLinkColumn ayant les caractéristiques suivantes :
Header Text | Désignation | Le texte de l’en-tête de la colonne |
Text Field | Code | Le champ correspondant de la base |
Url Field | ID_Produit | Le champ à rendre disponible dans l’URL |
Url Format String | ?id={0} | L’URL associée ({0} désignant Url Field) |
Créez une nouvelle colonne de type BoundColumn ayant les caractéristiques suivantes :
Header Text | Stocks | Le texte de l’en-tête de la colonne |
Data Field | Stocks | Le champ correspondant de la base |
Cliquez sur OK pour enregistrer les modifications.
Enfin, nous allons personnaliser l’aspect graphique de notre grille de données (couleur de l’en-tête, espacement entre les cellules, largeur des colonnes) : 1 Faites apparaître la boîte de dialogue de paramétrage de EtatStock (voir plus haut).
2 Cliquez sur Format afin de faire apparaître la feuille de propriétés relative au formatage.
3 Sélectionnez l’élément Header dans la liste Objects et spécifiez :
Back Color | #AAAADD |
Sélectionnez l’élément Columns dans la liste Objets et spécifiez les largeurs suivantes :
Colonne 0 (Code) | 50 |
Colonne 1 (Désignation) | 100 |
Colonne 2 (Stock) | 50 |
Pour la colonne 2 (Stocks), sélectionnez l’élément fils Items et spécifiez :
Horizontal alignement | Right |
Cliquez sur Borders afin de faire apparaître la feuille de propriétés relative aux bordures
Spécifiez les caractéristiques suivantes pour les bordures :
Cell padding | 3 |
Grid lines | Both |
Border color | Black |
Border width | 1 px |
Cliquez sur OK pour enregistrer les modifications.
Jetez un coup d’oeil à l’onglet HTML : vous constaterez que la partie relative au contrôle EtatStock a été modifiée en conséquence.