Combining Table Per Hierachy Strategy with Table Per Type Strategy in Entity Framework

For an e-learning project that I coach, the team is using Entity Framework 5 in conjunction with SQL Server. During the data model design, we were facing an interesting situation where we had to combine between both EF inheritance strategies: the table per type (TPT) strategy and the table per hierarchy strategy (TPH). In this article, I will share the technique that will allow us to use both techniques using fluent mapping. The data model consists of five classes (I am using a reduced version for simplicity and confidentiality reasons) : the resource which is a learning resource such as a question or a questionnaire. There are two types of questions : class A questions and class B questions. The data model is illustrated in the class diagram below : To persist the entities, we needed three tables : “Resource”, “Question” and “Questionnaire”. The first table contains the common fields shared by all the resources. In addition to that, the tables “Question” and “Questionnaires” will contain the specific data related to questions and questionnaires. A minimalistic version of the database is shown in the diagram below :   Every questionnaire will have its fields split between the table “Questionnaire” and “Resource”. Every question will have its fields split between the table “Question” and “Resource”. Knowing that the class Question is abstract (so is the class Resource), the two tables (Question and Resource) will persist instances of the two classes “QuestionClassA” and “QuestionClassB”. To achieve all this, we needed to combine the TPT and the TPH strategies in our mappings and as you could guess, it’s possible with Entity Framework. The first step is to write the mappings for the class “Resource” : public class ResourceMapping : EntityTypeConfiguration<Resource> { public ResourceMapping() { ToTable("Resource"); HasKey(e => e.Id); Property(e => e.Id) .HasColumnName("ResourceId") .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(e => e.Text) .HasColumnName("ResourceText") .IsRequired();   } } This is a very conventional mapping where we map an auto-generated int key and a string field. After the resource mapping, we will map the entity “Questionnaire” knowing that there is a “1….0..1”  relationship between the tables “Resource” and “Questionnaire”. In the questionnaire entity, there is an additional property called “QuestionnaireDate” mapped to a column with the same name. Below is the fluent mapping for the questionnaire : public class QuestionnaireMapping : EntityTypeConfiguration<Questionnaire> { public QuestionnaireMapping() { ToTable("Questionnaire"); Property(e => e.QuestionnaireDate); } } Notice the “ToTable” method that indicates that the fields are mapped to columns belonging to another table. That’s the key to the TPT strategy. Finally, and that’s the interesting part, the mapping of the entity“Question”. The mapping below not only indicates that the entity is mapped to another table, but indicates also that the two descendant types are mapped to this table using the “Map” method : public class QuestionMapping : EntityTypeConfiguration<Question> { public QuestionMapping() { ToTable("Question"); Property(e => e.AnswerId); Map<QuestionClassA>(e => e.Requires("QuestionType").HasValue<byte>(1)).ToTable("Question"); Map<QuestionClassB>(e => e.Requires("QuestionType").HasValue<byte>(2)).ToTable("Question"); } } The TPH is achieved using a descriminator column that is called “QuestionType”. A value of 1 of the descriminator correspond to a class A question while a value of 2 corresponds to a class B one. To test our mapping, I simply created a console application that creates sample data and lists it using the “Prepare” and “AddResource” methods : private static void Prepare(MyContext context) { AddResource<QuestionClassA>(context, "question A", e => e.AnswerId = 1); AddResource<QuestionClassB>(context, "question B", e => e.AnswerId = 5); AddResource<Questionnaire>(context, "questionnaire", e => e.QuestionnaireDate = DateTime.Now); context.SaveChanges(); }   private static void AddResource<T>(MyContext context, string text, Action<T> initMethod) where T : Resource, new() { T entity = new T() { Text = text }; if (initMethod != null) initMethod(entity); context.Resources.Add(entity); } Enjoy. You can find attached with this blog the source code and the database script : Download the code hereAnd the db script here

Tutoriel 7.1: Intégration d’AJAX dans les applications web

L’objectif de ce tutoriel (associé au module 7) est de montrer comment intégrer les technologies AJAX dans les applications ASP.NET. La première partie, utilise les dates pour montrer comment UpdatePanel permet uniquement à des portions de page d’être actualisées. La deuxième partie consiste en la création d’un service web et son invocation depuis le client en utilisant JavaScript et JQuery. Prérequis : Ce tutoriel requiert que la base de données « AdventureWorks » soit installée dans la machine. Cette base peut être téléchargée sur http://sqlserversamples.codeplex.com/ . Etape 1 – Préparation L’objectif de cette étape est de préparer l’application. Créer une nouvelle application ASP.NET vide appelée « TestAJAX » Créez trois pages dans la nouvelle application « Default.aspx », « Partiel.aspx » et « Service.aspx » Dans la page web « Default.aspx » ajoutez deux liens pointant respectivement sur « Partiel.aspx » et « Service.aspx » avec la propriété « Text » égale à : « Chargement Partiel » et « Service Web » respectivement. Ajoutez un modèle EntityFramework appelé « AdventureModel » pointant sur la base de données « AdventureWorks ». Le nom du contexte doit être « AdventureContext ». Pour plus de détails voir « Module 6 ». N’oubliez pas de cochez « Mettre au plusieurs ou au singulier les entités générées ». Le modèle inclut une seule table « Product » et génère une seule entité « Product » Ajoutez une feuille de style appelée « Site.css » Ajoutez les règles suivantes à la feuille de style : body { font-family: 'Gill Sans' , 'Gill Sans MT' , Calibri, 'Trebuchet MS' , sans-serif; } .asideBar { border: thin solid #919191; float: left; margin: 5px; background-color: #F8E9AD; padding : 5px; min-width : 200px; min-height : 400px; }   div.content { border: thin solid #0D86FF; float: left; margin: 5px; padding: 5px; }   div.clear { float : left; }   .loading { text-align: center; background-color: #C24B4B; color: #FFFFFF; margin-top: 2px; padding: 3px; } Ouvrez la page « Partiel.aspx » en mode source Insérez les balises suivantes après la balise « div » du formulaire (« form ») : <div> <aside class="asideBar"> </aside> <div class="content"> </div> <div class="clear" /> </div> Ouvrez la page « Partiel.aspx » en mode design Faites glisser le fichier « Site.css » sur la page afin d’appliquer le style. Ouvrez la console du gestionnaire de paquets « Nuget » Installez « JQuery » en entrant « Install-Package JQuery » Etape 2 : Implémentation de chargement partiel L’objectif de cette étape de permettre au développeur de voir l’effet du chargement partiel en comparant entre le contenu de deux libellés. Elle montre aussi l’utilisation de l’UpdatePanel en conjonction avec l’UpdateProgress. Ouvrez la page « Partiel.aspx » en mode design Cliquez sur la barre latérale « aside » puis insérer un « Label » dedans A partir de la barre à outils, ajoutez un « ScriptManager » (onglet extensions AJAX) en début de page Ajoutez une source de données « EntityDataSource » et pointez-la sur l’ensemble « Products » du contexte « AdventureContext ». Si vous n’arrivez pas à voir le contexte, compilez la solution en appuyant sur « F6 » Ajoutez un « UpdatePanel » dans la « div » dont la classe est « content » Insérez un deuxième « Label » à l’intérieur de l’UpdatePanel que vous venez d’ajouter Ajoutez un « GridView » à l’intérieur du composant « UpdatePanel » que vous venez d’ajouter. Configurez la source de données pour pointer sur la source de données « EntityDataSource » ajoutée précédemment. Configurez le « GridView » de façon à ce qu’il ne contienne que les colonnes « ProductID », « Name » et « Color » Configurez la pagination du GridView en affectant « True » à la propriété « AllowPaging » Ajoutez un composant « UpdateProgress » en dessous du « UpdatePanel » Dans la propriété « DisplayAfter », entrez 50 Dans la propriété « AssociatedUpdatepanelID » entrez « UpdatePanel1 » Affichez la page « Partiel.aspx » en mode source Dans la propriété « ProgressTemplate » de l’UpdateProgress insérez un div et du texte comme suit : <asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="50" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> <div class="loading">Chargement en cours...</div> </ProgressTemplate> </asp:UpdateProgress> La page en mode design doit être comme suit :   Le code ASPX généré doit être comme celui-ci : <form id="form1" runat="server"> <div>   <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:EntityDataSource ID="EntityDataSource1" runat="server" ConnectionString="name=AdventureContext" DefaultContainerName="AdventureContext" EnableFlattening="False" EntitySetName="Products"> </asp:EntityDataSource> <aside class="asideBar"> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </aside> <div class="content"> <asp:UpdatePanel ID="UpdatePanel1" runat="server" AssociatedUpdatePanelID="UpdatePanel1" UpdateMode="Conditional"> <ContentTemplate> <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label> <br /> <asp:GridView ID="GridView1" runat="server" BackColor="LightGoldenrodYellow" BorderColor="Tan" BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None" AllowPaging="True" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="EntityDataSource1"> <AlternatingRowStyle BackColor="PaleGoldenrod" /> <Columns> <asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID" /> <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" /> <asp:BoundField DataField="Color" HeaderText="Color" SortExpression="Color" /> </Columns> <FooterStyle BackColor="Tan" /> <HeaderStyle BackColor="Tan" Font-Bold="True" /> <PagerStyle BackColor="PaleGoldenrod" ForeColor="DarkSlateBlue" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" /> <SortedAscendingCellStyle BackColor="#FAFAE7" /> <SortedAscendingHeaderStyle BackColor="#DAC09E" /> <SortedDescendingCellStyle BackColor="#E1DB9C" /> <SortedDescendingHeaderStyle BackColor="#C2A47B" /> </asp:GridView> </ContentTemplate> </asp:UpdatePanel>   <asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="50" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> <div class="loading">Chargement en cours...</div> </ProgressTemplate> </asp:UpdateProgress>   </div> <div class="clear" />   </div> </form> Passez en mode « Code Behind » en appuyant sur « F7 » Dans la méthode « Page_Load », affectez la date en cours aux deux libellés comme suit : protected void Page_Load(object sender, EventArgs e) { Label1.Text = DateTime.Now.ToString(); Label2.Text = DateTime.Now.ToString(); } Exécutez en appuyant sur « F5 » et naviguez jusqu’à la page « Partiel.aspx » Remarquez qu’en changeant la page en cours dans la grille, la date ne change que pour un seul libellé alors que la page devait changer les deux Remarquez que le « UpdateProgress » s’affiche à chaque fois qu’un lien de pagination est cliqué Etape 3 : Création d’un service web WCF Dans le projet « TestAJAX », cliquez sur « Ajouter un Nouvel Elément » Dans le modèle sélectionnez « Service WCF AJAX » Dans la zone « Nom », entrez « ProductService » Appuyez sur « OK » Remarquez que VS génère une classe de service avec une seule méthode « DoWork » comme suit : [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class ProductService { // To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json) // To create an operation that returns XML, // add [WebGet(ResponseFormat=WebMessageFormat.Xml)], // and include the following line in the operation body: // WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml"; [OperationContract] public void DoWork() { // Add your operation implementation here return; }   // Add more operations here and mark them with [OperationContract] } Supprimez la méthode « DoWork » de la classe Ajoutez une méthode « GetProduct » décorée par l’attribut « OperationContract » L’attribut « OperationContract » stipule que la méthode peut être appelée comme service Changez le code de la méthode comme suit :   [OperationContract] public Product GetProduct(int id) { using (var context = new AdventureContext()) { return context.Products.FirstOrDefault(e => e.ProductID == id); } } Compilez en appuyant sur « F6 » et vérifiez qu’il n’y a pas d’erreur Ouvrez la page « Default.aspx » Exécutez en appuyant sur « F5 » Dans la barre d’adresse du navigateur, remplacez « Default.aspx » par « ProductService.svc » Remarquez les informations données sur le service permettant aux clients de l’invoquer. Etape 4 : Invocation du service web depuis JavaScript Le but de cette étape est d’utiliser le composant « ScriptManager » afin qu’il génère les proxies nécessaires à l’invocation du service « ProductService ». Pour invoquer le services, des évènements JQuery seront utilisés. Ouvrez la page « Service.aspx » en mode design Glissez un « ScriptManager » sur la page Cliquez sur la propriété « Services » du « ScriptManager » puis sur le bouton en pointillés qui apparaît Cliquez sur le bouton « Ajouter » A droite, dans la propriété « Path », entrez « ~/ProductService.svc » Cliquez sur « OK » Ouvrez la page « Service.aspx » en mode source Ajoutez trois paragraphes (« <p> ») dont les deux derniers ont l’attribut « class » à « info » Dans le premier paragraphe, ajoutez une balise « label » contenant le texte « Numéro : » Dans le premier paragraphe, ajoutez un contrôle HTML (pas ASP.NET) de type bouton avec la propriété « ID » pour « btnChercher » et l’attribut « value » à « Trouver Produit » Dans le deuxième paragraphe, ajoutez une balise « label » avec le texte « Nom : » Dans le deuxième paragraphe, ajoutez un contrôle HTML « input (Text ») avec la propriété « ID » à « txtNom » Dans le troisième paragraphe ajoutez une balise « label » avec le texte « Couleur : » Dans le deuxième paragraphe, ajoutez un contrôle HTML « input (Text ») avec la propriété « ID » à « txtNom » Le code devra ressembler au code qui suit : <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="~/ProductService.svc" /> </Services> </asp:ScriptManager> <p> <label> Trouver : <input id="txtNumero" type="text" /></label> <input id="btnChercher" type="button" value="Trouver Produit" /> </p> <p class="info"> <label> Nom : </label> <input id="txtNom" type="text" />   </p> <p class="info"> <label> Couleur : </label> <input id="txtCouleur" type="text" /> </p> </div> En mode source toujours, faites glisser le fichier « JQuey-x.y.z.js » dans la page juste avant la balise «/head » fermante où x.y.z est la version installée de JQuery En mode source, ajoutez le script « JavaScript » suivant : <script type="text/javascript"> $(function () { $(".info").hide(); $("#btnChercher").click(function () { var numero = $("#txtNumero").val(); ProductService.GetProduct(numero, function (data) { if (data == null) { alert('non trouvé !'); $(".info").hide(); } else { $("#txtNom").val(data.Name); $("#txtCouleur").val(data.Color); $(".info").show(); } }); }); }); </script> La ligne suivante permet d’invoquer l’opération « GetService » du service « GetProduct » avec comme paramètre la variable « numero ». Le deuxième paramètre est la fonction à appeler lorsque l’appel réussit. AJAX est bâti sur ce genre d’appel car le services s’exécutent d’une manière asynchrone, la fonction passée en paramètre est appelée « callback ». ProductService.GetProduct(numero, function (data) { if (data == null) { alert('non trouvé !'); $(".info").hide(); } else { $("#txtNom").val(data.Name); $("#txtCouleur").val(data.Color); $(".info").show(); } }); Exécutez l’application en appuyant sur « F5 » Pour télécharger le code du tutoriel, cliquez ici

Tutoriel 6.2 : Connexion à une base de données en utilisant Entity Framework

L’objectif de ce tutoriel (deuxième du module 6) est d’utiliser EntityFramework pour effectuer des requêtes sur une base de données. En introduisant une année et en cliquant sur un bouton, l’utilisateur affichera tous les films dont l’année est supérieure à l’année entrée. L’objectif de cette étape est de créer la page permettant la recherche et l’affichage des résultats Lancez VS 2012 Créez une application ASP.NET vide appelée « LinqToEntitiesTest » Ajoutez une forme web appelée « Default.aspx » Glissez un « Label » avec la propriété « Text » à « Année » Glissez un « TextBox » à droite du libellé avec la propriété « ID » à « txtSearch » Ajoutez un bouton à droite de la zone de texte avec la propriété « Text » à « Chercher » et « ID » à « btnSearch » A partir de l’onglet « Données », glissez un « GridView » en dessous des contrôles que vous venez d’ajouter Etape 2 : Création du contexte EntityFramework L’objectif de cette étape est de créer l’objet « Contexte » permettant de se connecter à la base de données de films. Dans l’explorateur de solutions, ajoutez un nouvel élément au projet « LinqToEntitiesTest » Dans les catégories à gauche, choisissez « Données » Dans le modèle, choisissez « Modèle Entité ADO.NET » Dans la zone « Nom » entrez « FormationModel » Cliquez sur le bouton « Ajouter » Visual Studio, demande si on doit générer le modèle à partir d’une base de données existante ou générer un nouveau modèle. Cliquez sur « Générer à partir d’une base de données » puis sur « Suivant » VS demande la chaîne de connexion à utiliser.   Si la chaîne n’existe pas, cliquez sur « Nouvelle Connexion ». Dans la zone serveur entrez « . » ou « .\SQLExpress » selon la version de SQL server installée Dans la zone « base de données » sélectionnez la base de données « Formation » Cliquez sur « Tester la connexion » pour valider Cliquez sur « OK » Cliquez sur « Suivant » Dans le nœud « Tables », cochez la table « Formation » Cochez « pluraliser ou singulariser les entités générées » Cliquez sur « Terminer » VS 2012 génère automatiquement le modèle et l’ouvre en mode conception Remarquez que le modèle contient une seule entité « Film » Etape 3 : Exécution de la requête en utilisant le contexte L’objectif de cette étape est d’utiliser le contexte généré par Visual Studio dans la page web afin de chercher les films dont l’année est supérieure à l’année fournie. Ouvrez la page « Default.aspx » en mode conception Double-cliquez sur le bouton afin de générer l’évènement de clic Dans l’évènement généré, insérez le code suivant: int numero = int.Parse(txtSearch.Text); using (FormationEntities context = new FormationEntities()) { var requete = from entite in context.Films where entite.Annee > numero select entite; GridView1.DataSource = requete.ToList(); GridView1.DataBind();   } Appuyez sur « F5 » pour exécuter Examinez la requête « Linq » et sa syntaxte var requete = from entite in context.Films where entite.Annee > numero select entite; Linq utilise une syntaxe proche du SQL pour permettre aux développeurs d’exécuter des requêtes sur des contextes qui sont traduites par EntityFramework en requêtes SQL Pour télécharger le code, cliquez ici

Tutoriel 6.1 : Connexion aux bases de données en utilisant ADO.NET

L’objectif de ce tutoriel (premier du module 6) est d’explorer certaines capacités de Visual Studio et ASP.NET à gérer et à créer des applications de bases de données. Etape 1 – Connexion à AdventureWorks L’objectif de cette étape est d’utiliser l’explorateur de serveur de Visual Studio 2012 dans le but de se connecter à une base de données. Ce tutoriel requiert que la base de données « AdventureWorks » soit jointe à SQL Server. Pour télécharger cette base, accédez au site : http://sqlserversamples.codeplex.com/ Lancez VS 2012 Dans le menu, sélectionnez « Affichage à Explorateur de Serveur » La fenêtre ci-dessous s’affiche Sur le nœud « Connexion de données », cliquez sur le bouton droit puis « Ajouter une connexion » La fenêtre ci-dessous s’affiche : A côté de la zone « Sources de données », cliquez sur « Changer » La fenêtre ci-dessous s’affiche Remarquez les bases de données auquel .NET peut accéder   Cliquez sur « Annuler » Dans la zone « Nom du serveur » tapez « . » ou « .\SQLExpress » selon la version de SQL Server dont vous disposez Dans la liste se connecter à la base, sélectionnez « AdventureWorksLT2008R2 »* Cliquez sur « Tester la connexion » pour vérifier que ça marche Cliquez sur « OK » Remarquez que VS a ajouté un nouveau nœud sous les connexions de données pointant sur la base de données « AdventureWorks » Développez le nouveau nœud créé Le nœud contient un enfant appelé « tables », développez-le et remarquez que les tables de la base de données sont listées Cliquez avec le bouton droit sur la table « Product » Cliquez sur « Afficher les Données de la table » Une grille s’affiche contenant tous les enregistrements de la table « Product » Etape 2 : Création d’une base de données L’objectif de cette étape est d’utiliser l’explorateur de serveur afin de créer une nouvelle base de données, d’y créer des tables et des données de test. Dans l’explorateur de serveur, cliquez avec le bouton droit sur « Connexions de données » Cliquez sur « Créer une nouvelle base de données SQL Server » Dans la zone « Nom du serveur », tapez « . » ou « .\SQLExpress » selon la version de SQL Server dont vous disposez Dans la zone « Nom de la nouvelle base de données », entrez « Formation » Cliquez sur « OK ». Dans l’explorateur de serveur, cliquez sur le nœud « Tables » de la nouvelle base de données avec le bouton droit Sélectionnez « Ajouter une nouvelle table » Ajoutez une colonne avec le nom « Numero », le type « int » qui n’accepte pas les valeurs vides Ajoutez une colonne avec le nom « Titre », le type « nvarchar(200) » qui n’accepte pas les vides Ajoutez une colonne avec le nom « Annee », le type « int » qui n’accepte pas les vides Cliquez avec le bouton droit sur la ligne contenant la colonne « Numero » et sélectionnez « Clé Primaire » Dans la barre à outils de VS, cliquez sur le bouton « Enregistrer » VS demande ensuite le nom de la table, entrez « Film » Cliquez sur « OK » Dans le nœud « Tables » dans l’explorateur de serveur, cliquez avec le bouton droit sur la table « Film » Sélectionnez « Afficher les données » En utilisant la fenêtre nouvellement affichées, saisissez quelques films Fermez la fenêtre Cliquez avec le bouton droit sur la base de données « Formation » dans l’explorateur de serveur Cliquez sur Propriétés La fenêtre de propriétés s’affiche Remarquez la propriété « Connection String », c’est cette propriété que va utiliser les applications web pour se connecter à la base de données Etape 3 : Accès à la base de données en mode connecté L’objectif de cette étape est d’utiliser l’API ADO.NET afin de se connecter à une base de données en mode connecté. L’affichage se fera suite à la création d’une connexion, d’une commande SQL ramenant tous les films puis la lecture et l’affichage des enregistrements un par un. Créez une application ASP.NET vide appelée « TestADO » Ajoutez une forme web appelée « Default.aspx » Ajoutez un composant « Literal » à la page « Default » Appuyez sur « F7 » pour accéder au code behind Modifiez l’évènement Page_Load comme suit : protected void Page_Load(object sender, EventArgs e) { int numero, annee; string titre, ligne; using (SqlConnection connection = new SqlConnection("chaine_connection")) { connection.Open(); using (DbCommand command = connection.CreateCommand()) { command.CommandText = "SELECT Numero, Titre, Annee FROM Film"; using (DbDataReader reader = command.ExecuteReader()) { while (reader.Read()) { numero = reader.GetInt32(0); titre = reader.GetString(1); annee = reader.GetInt32(2); ligne = string.Format("{0} | {1} | {2} <br/>", numero, titre, annee); Literal1.Text += ligne; } } } } } Remplacez « chaine_connection » par la chaîne de connexion de la base de données « Formation » que vous pouvez récupérer à partir de l’explorateur de serveur Appuyez sur « F5 » pour exécutez   Etape 4 : Accès à la base en mode déconnecté L’objectif de cette étape est de pouvoir utiliser les capacités d’ADO.NET de se connecter à une base de données en mode déconnecté. Il s’agira de la création d’un ensemble de données (DataSet) fortement typé puis de peupler une table à partir des résultats du « DataSet ». Ajoutez au projet une nouvelle forme web appelée « Connected.aspx » Cliquez sur le projet « TestADO » avec le bouton droit puis cliquez sur « Ajouter un nouvel élément » A gauche, sélectionnez « Données » Sélectionnez le modèle ensemble de données « DataSet » Dans la zone « Nom », entrez « FormationDataSet » Cliquez sur OK Visual Studio crée un ensemble de données vide Affichez l’explorateur de serveurs A partir de la base de données « Formation », glissez la table « Film » sur la fenêtre de l’ensemble de données Ouvrez la page « Connected.aspx » en mode conception A partir de la boîte à outils, faites glisser un composant « Table » depuis l’onglet « Standard » Appuyez sur « F7 » pour passer au vue code Changez le code de la méthode « Page_Load » comme suit : TableRow row; TableCell cell; protected void Page_Load(object sender, EventArgs e) { cell = new TableCell(); cell.Text = dataRow.Numero.ToString(); row.Cells.Add(cell); using (FormationDataSet dataSet = new FormationDataSet()) { using (FilmTableAdapter adapter = new FilmTableAdapter()) { adapter.Fill(dataSet.Film); } foreach (TestADO.FormationDataSet.FilmRow dataRow in dataSet.Film.Rows) { row = new TableRow(); cell = new TableCell(); cell.Text = dataRow.Numero.ToString(); row.Cells.Add(cell); cell = new TableCell(); cell.Text = dataRow.Titre; row.Cells.Add(cell); cell = new TableCell(); cell.Text = dataRow.Annee.ToString(); row.Cells.Add(cell); Table1.Rows.Add(row); } } } Appuyez sur « F5 » pour exécuter Pour télécharger le code associé, cliquez ici