using System; using System.Xml; using System.Xml.Serialization; namespace MonPremierTP { [Serializable][XmlType("carte-de-visite")] public class CarteDeVisite { [XmlElement("nom")] public string Nom; [XmlElement("prenom")] public string Prenom; [XmlElement("email")] public string Email; [NonSerialized()] public string Telephone; } }Nous verrons ultérieurement comment les différentes fiches seront initialisées par des données issues de la base de données. La description et l'utilisation des différents attributs sont présentés dans la section B.6.5. Pour génerer la bibliothèque CarteDeVisite.dll, la commande est la suivante :
csc /target:library CarteDeVisite.cs
// volontairement le namespace n'est pas répéter... // à vous de l'indiquer et d'utiliser les bonnes directives 'using' [assembly:AssemblyVersion("0.1.0.0")] public class AnnuaireDAL { public AnnuaireDAL() {} // constructeur public CarteDeVisite ExtraireCarte(string nomRecherche) { // opération de recherche d'une carte dans la BD return null; } public void AjouterCarte(CarteDeVisite carte){ // opération d'insertion d'une nouvelle carte } }Pour générer la bibliothèque AnnuaireDAL.dll, la commande est la suivante :
csc /target:library /reference:CarteDeVisite.dll AnnuaireDAL.cs
public interface IAnnuaireService { CarteDeVisite Rechercher(string nom); } public interface IAnnuaireAdminService { CarteDeVisite Rechercher(string nom); void Ajouter(CarteDeVisite carte); }Une fois ces interfaces définies, il ne nous reste qu'à les implanter en utilisant la couche d'accès aux données décrite précédemment. AnnuaireService (AnnuaireService.cs)
public class AnnuaireService : IAnnuaireService { // constructeur de la classe public AnnuaireService() {} // implémentation de la méthode métier public CarteDeVisite Rechercher(string nom) { AnnuaireDAL dal=new AnnuaireDAL(); return dal.ExtraireCarte(nom); } }
public class AnnuaireAdminService : IAnnuaireAdminService { // constructeur de la classe public AnnuaireAdminService(){} // implémentation de la méthode métier public CarteDeVisite Rechercher(string nom) { AnnuaireDAL dal=new AnnuaireDAL(); return dal.ExtraireCarte(nom); } public void Ajouter(CarteDeVisite carte) { AnnuaireDAL dal=new AnnuaireDAL(); // on vérifie que la carte n'existe pas déjà CarteDeVisite test=dal.ExtraireCarte(carte.Nom); if (test==null) dal.AjouterCarte(carte); else throw new Exception("La carte existe déjà"); } }
// xml_AnnuaireDAL.cs // à compiler par : csc /doc:xml_AnnuaireDAL.xml [assembly:AssemblyVersion("0.2.0.0")] /// Mon texte pour la classe AnnuaireDAL /// <summary> /// Description de la classe AnnuaireDAL . /// </summary> public class AnnuaireDAL { /// <remarks> /// Des commentaires plus longs peuvent être associés à un type ou un membre /// grâce à la balise remarks</remarks> /// <example> Cette classe permet d'isoler la partie métier de la persistance /// des données, elle comporte essentiellement deux méthodes. /// <code> /// public CarteDeVisite ExtraireCarte(string nomRecherche) /// public void AjouterCarte(CarteDeVisite carte) /// </code> /// </example> /// <summary> /// Constructeur de la classe AnnuaireDAL. /// </summary> public AnnuaireDAL() {} // constructeur /// texte pour la méthode ExtraireCarte /// <summary> /// Cette méthode très sophistiquée reste totalement à implémenter. Le paramètre /// d'entrée est <paramref name="nomRecherche" /> /// </summary> /// <param name="nomRecherche"> /// le paramètre d'entrée est le nom du titulaire de la carte de visite recherchée /// </param> /// <returns> /// le résultat est la carte de visite associée au nom /// <returns> /// <exception cref="NomInconnu"> /// Une exception est levée si le nom est inconnu /// </exception> public CarteDeVisite ExtraireCarte(string nomRecherche) { // opération de recherche d'une carte dans la BD return null; } /// texte pour la méthode AjouterCarte /// <summary> /// Description de AjouterCarte.</summary> /// <param name="carte"> Emplacement de la description du paramètre carte</param> /// <seealso cref="String"> /// Vous pouvez utiliser l'attribut cref sur n'importe quelle balise pour /// faire référence à un type ou un membre, /// et le compilateur vérifiera que cette référence existe. </seealso> public void AjouterCarte(CarteDeVisite carte){ // opération d'insertion d'une nouvelle carte } /// <summary> /// Une propriété introduite juste pour montrer la balise "value". /// </summary> /// <value>donne toujours 0492965148</value> public int UnePropriete { get { return 0492965148; } } }Mettez ce code dans le fichier commentaire1.cs et compilez le : csc.exe /doc:commentaire1.xml /reference:CarteDeVisite.dll commentaire1.cs et examinez le fichier XML produit. Maintenant, à vous de jouer en commentant les différentes classes précédemment écrites. Faites bien la différence entre les commentaires débutants par '///', pris en compte dans la génération du fichier de documentation XML et les commentaires débutant par '//' ou '/*', seulement présent dans le fichier source. Attention : lorsque vous générez la documentation, vous compiler aussi le code en question.
csc.exe /out:annuaire.dll /t:library /r:AnnuaireDAL.dll /r:CarteDeVisite.dll \ AnnuaireService.cs IAnnuaireService.csObservez avec ildasm la dll produite et commentez. Qu'observez-vous : nombre de classes, fonctions membres ?
ildasm.exe annuaire.dll
using System; using System.IO; using System.Reflection; public class Meta { public static int Main () { // lire l'assembly Assembly a = Assembly.LoadFrom ("annuaire.dll"); // lire les modules de l'assembly Module[] modules = a.GetModules(); // inspecter tous les modules de l'assembly foreach (Module module in modules) { Console.WriteLine("Dans le module {0}, on trouve", module.Name); // lire tous les types du module Type[] types = module.GetTypes(); // inspecter tous les types foreach (Type type in types) { Console.WriteLine("Le type {0} a cette(ces) méthode(s) : ", type.Name); // inspecter toutes les méthodes du type MethodInfo[] mInfo = type.GetMethods(); foreach (MethodInfo mi in mInfo) { Console.WriteLine (" {0}", mi); } } } return 0; } }Compilez cette classe (csc <nom de fichier>.cs) et exécutez le code, puis faites une lecture attentive du source. Avez-vous tout compris ?
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Xml; using System.Xml.Serialization; class Serialisation { static void Main(string[] args) { CarteDeVisite uneCarte = new CarteDeVisite(); uneCarte.Nom = "RIVEILL"; uneCarte.Prenom = "Michel"; uneCarte.Email = "riveill@unice.fr"; uneCarte.Telephone = "04 92 96 51 48"; // on sérialise en binaire et on sauvegarde dans un fichier Stream stream = File.Open("Demo.bin", FileMode.Create); BinaryFormatter formatter = new BinaryFormatter (); formatter.Serialize (stream, uneCarte); stream.Close(); // on sérialise en XML et on sauvegarde dans un fichier Stream stream2 = File.Open("Demo.xml", FileMode.Create); XmlSerializer serializer = new XmlSerializer (typeof(CarteDeVisite)); serializer.Serialize(stream2, uneCarte); stream2.Close(); // on désérialise depuis un fichier (mode binaire) stream = File.Open ("Demo.bin", FileMode.Open); formatter = new BinaryFormatter(); CarteDeVisite obj = (CarteDeVisite) formatter.Deserialize (stream); stream.Close (); // on désérialise depuis un fichier (mode XML) stream2 = File.Open ("Demo.xml", FileMode.Open); serializer = new XmlSerializer (typeof(CarteDeVisite)); CarteDeVisite obj2 = (CarteDeVisite) serializer.Deserialize (stream2); stream2.Close (); Console.WriteLine ("\t\torigine\t\tbinaire\t\tXml"); // Nom, prenom et email ont bien été sérialisés Console.WriteLine ("uneCarte.nom (a ete serialisee)"); Console.WriteLine ("\t\t{0}\t{1}\t{2}", uneCarte.Nom, obj.Nom, obj2.Nom); Console.WriteLine ("uneCarte.prenom (a ete serialisee)"); Console.WriteLine ("\t\t{0}\t{1}\t{2}", uneCarte.Prenom, obj.Prenom, obj2.Prenom); Console.WriteLine ("uneCarte.email (a ete serialisee)"); Console.WriteLine ("\t\t{0}\t{1}\t{2}", uneCarte.Email, obj.Email, obj2.Email); // téléphone n'a pas été sérialisé Console.WriteLine ("uneCarte.telephone (n'a pas ete serialisee)"); Console.WriteLine ("\t\t{0}\t\t{1}\t\t{2}", uneCarte.Telephone, obj.Telephone, obj2.Telephone); } }Compilez et exécutez ce code. Analysez-le. Remarque : Si vous sérialisez un tableau ou une collection, tous les objets du tableau ou de la collection sont sérialisés.
string Nom; string Prenom; string Email; string Telephone;Par exemple la table obtenue peut avoir le format décrit dans la figure B.2.
listeCartes.DataSource = ExtraireCarteSQL (nom.Text); listeCartes.DataBind(); listeCartes.Visible = true;Petites explications : la première ligne met à jour la liste des données du DataSet en appelant la méthode ExtraireCarteSQL construite dans l'étape 6 précédente, le paramètre est le texte de la TextBox contenue dans la page. Les deux lignes mettent à jour le DataSet et le rendent visible. Attention : cette méthode, certes très efficace en terme de programmation ne respecte pas du tout l'architecture initiale, elle a juste permis d'utiliser la base de données créée et de nous familiariser avec Web Matrix pour la création de page web dynamique. La suite du TP reprend la construction pas à pas des différentes couches de l'application cartes de visite.
public CarteDeVisite ExtraireCarte(string nomRecherche) { CarteDeVisite carte = new CarteDeVisite (); System.Data.DataSet requete = ExtraireCarteSQL (nomRecherche); carte.Nom = requete.Tables["Table"].Rows[0]["nom"]; carte.Prenom = requete.Tables["Table"].Rows[0]["prenom"]; carte.Email = requete.Tables["Table"].Rows[0]["email"]; carte.Telephone = requete.Tables["Table"].Rows[0]["telephone"]; return carte; } public void AjouterCarte(CarteDeVisite carte) { AjouterCarteSQL (carte.Nom, carte.Prenom, carte.Email, carte.Telephone); }
REM production des différents modules csc /t:module CarteDeVisite.cs csc /t:module /addModule:CarteDeVisite.netmodule AnnuaireDAL.cs csc /t:module /addModule:CarteDeVisite.netmodule IAnnuaireService.cs REM liaison des modules produits avec la logique métier csc /out:AnnuaireService2.dll /t:library /addModule:IAnnuaireService.netmodule \ /addModule:CarteDeVisite.netmodule /addmodule:AnnuaireDAL.netmodule AnnuaireService.csVoici un exemple de compilation globale :
csc /out:AnnuaireService.dll /t:library IAnnuaireService.cs CarteDeVisite.cs \ AnnuaireDAL.cs AnnuaireService.cs
public class ClientLocal { public ClientLocal() { } static int Main() { try { IAnnuaireAdminService annuaire = new AnnuaireAdminService (); CarteDeVisite carte = annuaire.Rechercher ("RIVEILL"); Console.WriteLine ("l'email de RIVEILL est {0}", carte.Email); } catch (Exception e) { Console.WriteLine (e.ToString()); } return 0; } }
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class AnnuaireServiceRemoting: MarshalByRefObject, IAnnuaireService { IAnnuaireAdminService annuaire = new AnnuaireAdminService (); public CarteDeVisite Rechercher(string nom) { return annuaire.Rechercher(nom); } public void Ajouter(CarteDeVisite carte) { annuaire.Ajouter(carte); } }Server (Server.cs)
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; public class Server { [STAThread] static void Main(string[] args) { IChannel chan = new TcpChannel (7000); ChannelServices.RegisterChannel (chan, false); RemotingConfiguration.RegisterWellKnownServiceType( typeof (AnnuaireServiceRemoting), "TcpService", WellKnownObjectMode.Singleton); Console.WriteLine("Press Enter to disconnect the annuaire."); Console.ReadLine(); } }
<%@ WebService language="C#" class="AnnuaireWebService" Debug="true" %> using System; using System.Web.Services; using System.Xml.Serialization; public class AnnuaireWebService : System.Web.Services.WebService, IAnnuaireService { private IAnnuaireService service; public AnnuaireWebService() { service=new AnnuaireService(); } [WebMethod] public CarteDeVisite Rechercher(string nom) { return service.Rechercher(nom); } }
http://localhost:80/AnnuaireWebService.asmx pour avoir accès au service web http://localhost:8086/AnnuaireWebService.asmx?WSDL pour avoir accès à la description WSDL
wsdl.exe /out:proxy_webservice.cs /n:proxy http://machine:port/URL?WSDL REM la directive /n permet de préciser l'espace de nomVoici de manière comparative les différentes manières de construire le client : Client local
class Client { static void Main (string[] args) { // creation de l'objet IAnnuaireService annuaire = new AnnuaireService (); CarteDeVisite c = annuaire.Recherche ("auteur"); } }Client .NET Remoting
class Client { static void Main (string[] args) { // creation d'une connection TCP TcpChannel channel = new TcpChannel (); ChannelServices.RegisterChannel (channel, false); // creation du proxy IAnnuaireService annuaire = (IAnnuaireService) Activator.GetObject (typeof (IAnnuaireService), "tcp://127.0.0.1:7000/TcpService"); CarteDeVisite c = annuaire.Recherche ("auteur"); } }Client service web
class Client { static void Main (string[] args) { // creation du proxy proxy.AnnuaireWebService annuaire = new proxy.AnnuaireWebService (); proxy.cartedevisite c = annuaire.Recherche ("auteur"); } }
<%@ Page Language="C#" Debug="true" %> <%@ assembly Src="proxy_annuaireWS.cs" %>La partie code est la suivante :
void Button_Click(object sender, EventArgs e) { // création du proxy et appel du service web proxy.AnnuaireWebService annuaire = new proxy.AnnuaireWebService (); proxy.cartedevisite carte = annuaire.Rechercher (nom.Text); // mise à jour des 'Label' contenu dans la page LabelPrenomValeur.Text = carte.Prenom; LabelEmailValeur.Text = carte.Email; // rendre visible les 'Label" de la page LabelPrenom.Visible = true; LabelPrenomValeur.Visible = true; LabelEmail.Visible = true; LabelEmailValeur.Visible = true; }La partie 'html' de la page contient un TextBox pour saisir le nom et quatre Label : deux pour afficher le texte "prénom" et ëmail" et deux autres pour afficher les valeurs associées.
... <asp:TextBox id="nom" runat="server"></asp:TextBox> ... <asp:Label id="LabelPrenom" runat="server" text="Prénom : " \ visible="False"></asp:Label> <asp:Label id="LabelPrenomValeur" runat="server" text="Label" \ visible="False"></asp:Label> ... <asp:Label id="LabelEmail" runat="server" text="Email : " \ visible="False"></asp:Label> <asp:Label id="LabelEmailValeur" runat="server" text="Label" \ visible="False"></asp:Label>Solution 2 Créer un client léger qui utilise directement la dll précédemment produite. Ceci peut être fait très facilement en déplaçant la dll dans un répertoire bin, puis en utilisant la directive 'import' pour charger le 'namespace' du TP :
<%@ import Namespace="TP" %>Attention : si votre dll utilise des modules, il faut aussi déplacer dans ce répertoire les différents modules. Le code est le suivant (très voisin de l'étape précédente... en l'absence de la directive import, il est aussi possible d'utiliser les noms de classe complet) :
void Button_Click(object sender, EventArgs e) { IAnnuaireService annuaire = new AnnuaireService (); CarteDeVisite carte = annuaire.Rechercher (nom.Text); LabelPrenomValeur.Text = carte.Prenom; LabelEmailValeur.Text = carte.Email; LabelPrenom.Visible = true; LabelPrenomValeur.Visible = true; LabelEmail.Visible = true; LabelEmailValeur.Visible = true; }