Partager via


Prise en main avec Entity Framework 4.0 Database First et ASP.NET 4 Web Forms - Partie 4

par Tom Dykstra

L’exemple d’application web Contoso University montre comment créer des applications ASP.NET Web Forms à l’aide d’Entity Framework 4.0 et de Visual Studio 2010. Pour plus d’informations sur la série de didacticiels, consultez le premier didacticiel de la série

Dans le tutoriel précédent, vous avez utilisé le EntityDataSource contrôle pour filtrer, trier et regrouper des données. Dans ce tutoriel, vous allez afficher et mettre à jour les données associées.

Vous allez créer la page Instructeurs qui affiche une liste d’instructeurs. Lorsque vous sélectionnez un instructeur, vous voyez une liste des cours enseignés par cet instructeur. Lorsque vous sélectionnez un cours, vous voyez les détails du cours et une liste des étudiants inscrits au cours. Vous pouvez modifier le nom de l’instructeur, la date d’embauche et l’affectation du bureau. L’affectation office est un jeu d’entités distinct auquel vous accédez via une propriété de navigation.

Vous pouvez lier master données pour détailler les données dans le balisage ou dans le code. Dans cette partie du tutoriel, vous allez utiliser les deux méthodes.

Image01

Créez une page web nommée Instructors.aspx qui utilise la page master Site.Master et ajoutez le balisage suivant au Content contrôle nommé Content2:

<h2>Instructors</h2>
    <div>
        <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People"
            Where="it.HireDate is not null" Include="OfficeAssignment" EnableUpdate="True">
        </asp:EntityDataSource>
    </div>

Ce balisage crée un EntityDataSource contrôle qui sélectionne les instructeurs et active les mises à jour. L’élément div configure le balisage pour qu’il s’affiche à gauche afin que vous puissiez ajouter une colonne à droite ultérieurement.

Entre le EntityDataSource balisage et la balise de fermeture </div> , ajoutez le balisage suivant qui crée un GridView contrôle et un Label contrôle que vous utiliserez pour les messages d’erreur :

<asp:GridView ID="InstructorsGridView" runat="server" AllowPaging="True" AllowSorting="True"
            AutoGenerateColumns="False" DataKeyNames="PersonID" DataSourceID="InstructorsEntityDataSource"
            OnSelectedIndexChanged="InstructorsGridView_SelectedIndexChanged" 
            SelectedRowStyle-BackColor="LightGray" 
            onrowupdating="InstructorsGridView_RowUpdating">
            <Columns>
                <asp:CommandField ShowSelectButton="True" ShowEditButton="True" />
                <asp:TemplateField HeaderText="Name" SortExpression="LastName">
                    <ItemTemplate>
                        <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("LastName") %>'></asp:Label>,
                        <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("FirstMidName") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorLastNameTextBox" runat="server" Text='<%# Bind("FirstMidName") %>' Width="7em"></asp:TextBox>
                        <asp:TextBox ID="InstructorFirstNameTextBox" runat="server" Text='<%# Bind("LastName") %>' Width="7em"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Hire Date" SortExpression="HireDate">
                    <ItemTemplate>
                        <asp:Label ID="InstructorHireDateLabel" runat="server" Text='<%# Eval("HireDate", "{0:d}") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorHireDateTextBox" runat="server" Text='<%# Bind("HireDate", "{0:d}") %>' Width="7em"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Office Assignment" SortExpression="OfficeAssignment.Location">
                    <ItemTemplate>
                        <asp:Label ID="InstructorOfficeLabel" runat="server" Text='<%# Eval("OfficeAssignment.Location") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorOfficeTextBox" runat="server" 
                        Text='<%# Eval("OfficeAssignment.Location") %>' Width="7em"
                        oninit="InstructorOfficeTextBox_Init"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
            </Columns>
            <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
        </asp:GridView>
        <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label>

Ce GridView contrôle active la sélection de ligne, met en surbrillance la ligne sélectionnée avec une couleur d’arrière-plan gris clair et spécifie des gestionnaires (que vous créerez ultérieurement) pour les SelectedIndexChanged événements et Updating . Il spécifie PersonID également pour la DataKeyNames propriété , afin que la valeur de clé de la ligne sélectionnée puisse être passée à un autre contrôle que vous ajouterez ultérieurement.

La dernière colonne contient l’affectation du bureau de l’instructeur, qui est stockée dans une propriété de navigation de l’entité Person , car elle provient d’une entité associée. Notez que l’élément EditItemTemplate spécifie Eval au lieu de Bind, car le GridView contrôle ne peut pas lier directement aux propriétés de navigation pour les mettre à jour. Vous allez mettre à jour l’affectation office dans le code. Pour ce faire, vous aurez besoin d’une référence au TextBox contrôle, et vous l’obtiendrez et l’enregistrerez dans l’événement TextBox du Init contrôle.

Suivant le GridView contrôle se trouve un Label contrôle qui est utilisé pour les messages d’erreur. La propriété du Visible contrôle est false, et l’état d’affichage est désactivé, de sorte que l’étiquette s’affiche uniquement lorsque le code la rend visible en réponse à une erreur.

Ouvrez le fichier Instructors.aspx.cs et ajoutez l’instruction suivante using :

using ContosoUniversity.DAL;

Ajoutez un champ de classe privée immédiatement après la déclaration de nom de classe partielle pour contenir une référence à la zone de texte d’affectation de bureau.

private TextBox instructorOfficeTextBox;

Ajoutez un stub pour le SelectedIndexChanged gestionnaire d’événements que vous allez ajouter du code pour plus tard. Ajoutez également un gestionnaire pour l’événement du contrôle d’affectation TextBox de Init bureau afin de pouvoir stocker une référence au TextBox contrôle. Vous allez utiliser cette référence pour obtenir la valeur entrée par l’utilisateur afin de mettre à jour l’entité associée à la propriété de navigation.

protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
}

protected void InstructorOfficeTextBox_Init(object sender, EventArgs e)
{
    instructorOfficeTextBox = sender as TextBox;
}

Vous allez utiliser l’événement GridView du Updating contrôle pour mettre à jour la Location propriété de l’entité associée OfficeAssignment . Ajoutez le gestionnaire suivant pour l’événement Updating :

protected void InstructorsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    using (var context = new SchoolEntities())
    {
        var instructorBeingUpdated = Convert.ToInt32(e.Keys[0]);
        var officeAssignment = (from o in context.OfficeAssignments
                                where o.InstructorID == instructorBeingUpdated
                                select o).FirstOrDefault();

        try
        {
            if (String.IsNullOrWhiteSpace(instructorOfficeTextBox.Text) == false)
            {
                if (officeAssignment == null)
                {
                    context.OfficeAssignments.AddObject(OfficeAssignment.CreateOfficeAssignment(instructorBeingUpdated, instructorOfficeTextBox.Text, null));
                }
                else
                {
                    officeAssignment.Location = instructorOfficeTextBox.Text;
                }
            }
            else
            {
                if (officeAssignment != null)
                {
                    context.DeleteObject(officeAssignment);
                }
            }
            context.SaveChanges();
        }
        catch (Exception)
        {
            e.Cancel = true;
            ErrorMessageLabel.Visible = true;
            ErrorMessageLabel.Text = "Update failed.";
            //Add code to log the error.
        }
    }
}

Ce code est exécuté lorsque l’utilisateur clique sur Mettre à jour dans une GridView ligne. Le code utilise LINQ to Entities pour récupérer l’entité OfficeAssignment associée à l’entité actuellePerson, en utilisant le PersonID de la ligne sélectionnée à partir de l’argument d’événement.

Le code effectue ensuite l’une des actions suivantes en fonction de la valeur dans le InstructorOfficeTextBox contrôle :

  • Si la zone de texte a une valeur et qu’aucune entité n’est OfficeAssignment à mettre à jour, elle en crée une.
  • Si la zone de texte a une valeur et qu’il existe une OfficeAssignment entité, elle met à jour la valeur de la Location propriété.
  • Si la zone de texte est vide et qu’une OfficeAssignment entité existe, l’entité est supprimée.

Après cela, il enregistre les modifications apportées à la base de données. Si une exception se produit, un message d’erreur s’affiche.

Exécutez la page.

Image02

Cliquez sur Modifier et tous les champs passent aux zones de texte.

Image03

Modifiez l’une de ces valeurs, y compris l’affectation office. Cliquez sur Mettre à jour pour voir les modifications répercutées dans la liste.

Chaque instructeur peut enseigner un ou plusieurs cours. Vous allez donc ajouter un EntityDataSource contrôle et un GridView contrôle pour répertorier les cours associés à l’instructeur sélectionné dans le contrôle instructeurs GridView . Pour créer un en-tête et le contrôle pour les EntityDataSource entités de cours, ajoutez le balisage suivant entre le contrôle de message Label d’erreur et la balise de fermeture </div> :

<h3>Courses Taught</h3>
        <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="Courses" 
            Where="@PersonID IN (SELECT VALUE instructor.PersonID FROM it.People AS instructor)">
            <WhereParameters>
                <asp:ControlParameter ControlID="InstructorsGridView" Type="Int32" Name="PersonID" PropertyName="SelectedValue" />
            </WhereParameters>
        </asp:EntityDataSource>

Le Where paramètre contient la valeur de l’instructeur PersonID dont la ligne est sélectionnée dans le InstructorsGridView contrôle . La Where propriété contient une commande de sous-sélection qui obtient toutes les entités associées Person à partir de la propriété de navigation d’une CoursePeople entité et ne sélectionne l’entité Course que si l’une des entités associées Person contient la valeur sélectionnée PersonID .

Pour créer le GridView contrôle, ajoutez le balisage suivant immédiatement après le CoursesEntityDataSource contrôle (avant la balise de fermeture </div> ) :

<asp:GridView ID="CoursesGridView" runat="server" 
            DataSourceID="CoursesEntityDataSource"
            AllowSorting="True" AutoGenerateColumns="False"
            SelectedRowStyle-BackColor="LightGray" 
            DataKeyNames="CourseID">
            <EmptyDataTemplate>
                <p>No courses found.</p>
            </EmptyDataTemplate>
            <Columns>
                <asp:CommandField ShowSelectButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:TemplateField HeaderText="Department" SortExpression="DepartmentID">
                    <ItemTemplate>
                        <asp:Label ID="GridViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

Étant donné qu’aucun cours ne s’affiche si aucun instructeur n’est sélectionné, un EmptyDataTemplate élément est inclus.

Exécutez la page.

Image04

Sélectionnez un instructeur auquel un ou plusieurs cours sont attribués, et le ou les cours apparaissent dans la liste. (Remarque : bien que le schéma de base de données autorise plusieurs cours, dans les données de test fournies avec la base de données, aucun instructeur n’a réellement plus d’un cours. Vous pouvez ajouter vous-même des cours à la base de données à l’aide de la fenêtre Server Explorer ou de la page CoursesAdd.aspx, que vous ajouterez dans un didacticiel ultérieur.)

Image05

Le CoursesGridView contrôle affiche seulement quelques champs de cours. Pour afficher tous les détails d’un cours, vous allez utiliser un DetailsView contrôle pour le cours que l’utilisateur sélectionne. Dans Instructors.aspx, ajoutez le balisage suivant après la balise fermante </div> (veillez à placer ce balisage après la balise div fermante, et non avant celle-ci) :

<div>
        <h3>Course Details</h3>
        <asp:EntityDataSource ID="CourseDetailsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="Courses"
            AutoGenerateWhereClause="False" Where="it.CourseID = @CourseID" Include="Department,OnlineCourse,OnsiteCourse,StudentGrades.Person"
            OnSelected="CourseDetailsEntityDataSource_Selected">
            <WhereParameters>
                <asp:ControlParameter ControlID="CoursesGridView" Type="Int32" Name="CourseID" PropertyName="SelectedValue" />
            </WhereParameters>
        </asp:EntityDataSource>
        <asp:DetailsView ID="CourseDetailsView" runat="server" AutoGenerateRows="False"
            DataSourceID="CourseDetailsEntityDataSource">
            <EmptyDataTemplate>
                <p>
                    No course selected.</p>
            </EmptyDataTemplate>
            <Fields>
                <asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
                <asp:TemplateField HeaderText="Department">
                    <ItemTemplate>
                        <asp:Label ID="DetailsViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Location">
                    <ItemTemplate>
                        <asp:Label ID="LocationLabel" runat="server" Text='<%# Eval("OnsiteCourse.Location") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="URL">
                    <ItemTemplate>
                        <asp:Label ID="URLLabel" runat="server" Text='<%# Eval("OnlineCourse.URL") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Fields>
        </asp:DetailsView>
    </div>

Ce balisage crée un EntityDataSource contrôle lié au jeu d’entités Courses . La Where propriété sélectionne un cours à l’aide de la CourseID valeur de la ligne sélectionnée dans le contrôle courses GridView . Le balisage spécifie un gestionnaire pour l’événement Selected , que vous utiliserez ultérieurement pour afficher les notes des étudiants, qui est un autre niveau inférieur dans la hiérarchie.

Dans Instructors.aspx.cs, créez le stub suivant pour la CourseDetailsEntityDataSource_Selected méthode . (Vous remplirez ce stub plus loin dans le tutoriel ; pour l’instant, vous en avez besoin pour que la page soit compilée et exécutée.)

protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}

Exécutez la page.

Image06

Au départ, il n’y a pas de détails sur le cours, car aucun cours n’est sélectionné. Sélectionnez un instructeur auquel un cours est attribué, puis sélectionnez un cours pour afficher les détails.

Image07

Enfin, vous souhaitez afficher tous les étudiants inscrits et leurs notes pour le cours sélectionné. Pour ce faire, vous allez utiliser l’événement Selected du EntityDataSource contrôle lié au cours DetailsView.

Dans Instructors.aspx, ajoutez le balisage suivant après le DetailsView contrôle :

<h3>Student Grades</h3>
        <asp:ListView ID="GradesListView" runat="server">
            <EmptyDataTemplate>
                <p>No student grades found.</p>
            </EmptyDataTemplate>
            <LayoutTemplate>
                <table border="1" runat="server" id="itemPlaceholderContainer">
                    <tr runat="server">
                        <th runat="server">
                            Name
                        </th>
                        <th runat="server">
                            Grade
                        </th>
                    </tr>
                    <tr id="itemPlaceholder" runat="server">
                    </tr>
                </table>
            </LayoutTemplate>
            <ItemTemplate>
                <tr>
                    <td>
                        <asp:Label ID="StudentLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>' />,
                        <asp:Label ID="StudentFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>' />
                    </td>
                    <td>
                        <asp:Label ID="StudentGradeLabel" runat="server" Text='<%# Eval("Grade") %>' />
                    </td>
                </tr>
            </ItemTemplate>
        </asp:ListView>

Ce balisage crée un ListView contrôle qui affiche une liste des étudiants et leurs notes pour le cours sélectionné. Aucune source de données n’est spécifiée, car vous allez lier le contrôle dans le code. L’élément EmptyDataTemplate fournit un message à afficher lorsqu’aucun cours n’est sélectionné. Dans ce cas, aucun étudiant n’est à afficher. L’élément LayoutTemplate crée un tableau HTML pour afficher la liste, et spécifie ItemTemplate les colonnes à afficher. L’ID d’étudiant et la note de l’étudiant proviennent de l’entité StudentGrade , et le nom de l’étudiant provient de l’entité Person que Entity Framework rend disponible dans la Person propriété de navigation de l’entité StudentGrade .

Dans Instructors.aspx.cs, remplacez la méthode stubbed-out CourseDetailsEntityDataSource_Selected par le code suivant :

protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
    var course = e.Results.Cast<Course>().FirstOrDefault();
    if (course != null)
    {
        var studentGrades = course.StudentGrades.ToList();
        GradesListView.DataSource = studentGrades;
        GradesListView.DataBind();
    }
}

L’argument événement de cet événement fournit les données sélectionnées sous la forme d’une collection, qui aura zéro élément si rien n’est sélectionné ou un élément si une Course entité est sélectionnée. Si une Course entité est sélectionnée, le code utilise la First méthode pour convertir la collection en un seul objet. Il obtient StudentGrade ensuite les entités de la propriété de navigation, les convertit en collection et lie le GradesListView contrôle à la collection.

Cela est suffisant pour afficher les notes, mais vous souhaitez vous assurer que le message dans le modèle de données vide s’affiche la première fois que la page est affichée et chaque fois qu’un cours n’est pas sélectionné. Pour ce faire, créez la méthode suivante, que vous allez appeler à partir de deux emplacements :

private void ClearStudentGradesDataSource()
{
    var emptyStudentGradesList = new List<StudentGrade>();
    GradesListView.DataSource = emptyStudentGradesList;
    GradesListView.DataBind();
}

Appelez cette nouvelle méthode à partir de la Page_Load méthode pour afficher le modèle de données vide la première fois que la page est affichée. Et appelez-le à partir de la InstructorsGridView_SelectedIndexChanged méthode, car cet événement est déclenché lorsqu’un instructeur est sélectionné, ce qui signifie que les nouveaux cours sont chargés dans le contrôle cours GridView et qu’aucun n’est encore sélectionné. Voici les deux appels :

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        ClearStudentGradesDataSource();                
    }
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
    ClearStudentGradesDataSource();
}

Exécutez la page.

Image08

Sélectionnez un instructeur auquel un cours est attribué, puis sélectionnez le cours.

Image09

Vous avez maintenant vu plusieurs façons d’utiliser des données associées. Dans le tutoriel suivant, vous allez apprendre à ajouter des relations entre des entités existantes, à supprimer des relations et à ajouter une nouvelle entité qui a une relation à une entité existante.