Condividi tramite


Introduzione a Entity Framework 4.0 Database First e a ASP.NET 4 Web Forms - Parte 4

di Tom Dykstra

L'applicazione Web di esempio Contoso University illustra come creare ASP.NET applicazioni Web Form usando Entity Framework 4.0 e Visual Studio 2010. Per informazioni sulla serie di esercitazioni, vedere la prima esercitazione della serie

Nell'esercitazione precedente è stato usato il EntityDataSource controllo per filtrare, ordinare e raggruppare i dati. In questa esercitazione verranno visualizzati e aggiornati i dati correlati.

Creerai la pagina Istruttori che mostra un elenco di istruttori. Quando si seleziona un insegnante, viene visualizzato un elenco di corsi insegnati da tale insegnante. Quando si seleziona un corso, vengono visualizzati i dettagli del corso e un elenco di studenti iscritti al corso. È possibile modificare il nome dell'insegnante, la data di assunzione e l'assegnazione dell'ufficio. L'assegnazione dell'ufficio è un insieme di entità separato a cui si accede tramite una proprietà di navigazione.

È possibile collegare i dati master ai dati dettagliati nel markup o nel codice. In questa parte dell'esercitazione si useranno entrambi i metodi.

Immagine01

Creare una nuova pagina Web denominata Instructors.aspx che usa la pagina master Site.Master e aggiungere il markup seguente al Content controllo denominato 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>

Questo markup crea un EntityDataSource controllo che seleziona gli insegnanti e abilita gli aggiornamenti. L'elemento div configura il markup per il rendering a sinistra in modo che sia possibile aggiungere una colonna a destra in un secondo momento.

Tra il EntityDataSource markup e il tag di chiusura </div> , aggiungere il markup seguente che crea un GridView controllo e un Label controllo che verrà usato per i messaggi di errore:

<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>

Questo GridView controllo abilita la selezione di righe, evidenzia la riga selezionata con un colore di sfondo grigio chiaro e specifica i gestori (che verranno creati in un secondo momento) per gli SelectedIndexChanged eventi e Updating . Specifica anche PersonID per la DataKeyNames proprietà , in modo che il valore della chiave della riga selezionata possa essere passato a un altro controllo che verrà aggiunto in un secondo momento.

L'ultima colonna contiene l'assegnazione dell'ufficio dell'insegnante, archiviata in una proprietà di navigazione dell'entità Person perché proviene da un'entità associata. Si noti che l'elemento EditItemTemplate specifica Eval invece di Bind, perché il GridView controllo non può essere associato direttamente alle proprietà di navigazione per aggiornarle. Aggiornerai l'assegnazione dell'ufficio nel codice. Per fare ciò, è necessario ottenere un riferimento al controllo nell'evento del controllo e salvarlo.

Dopo il GridView controllo è riportato un Label controllo utilizzato per i messaggi di errore. La proprietà Visible del controllo è false, e lo stato di visualizzazione è disattivato, quindi l'etichetta apparirà solo quando il codice la rende visibile in risposta a un errore.

Aprire il file Instructors.aspx.cs e aggiungere l'istruzione seguente using :

using ContosoUniversity.DAL;

Aggiungere un campo di classe privata immediatamente dopo la dichiarazione del nome parziale della classe per contenere un riferimento alla casella di testo per l'assegnazione dell'ufficio.

private TextBox instructorOfficeTextBox;

Aggiungere uno stub per il SelectedIndexChanged gestore eventi a cui si aggiungerà il codice successivamente. Aggiungere anche un gestore per l'evento del controllo di assegnazione TextBox dell'ufficio Init in modo che sia possibile archiviare un riferimento al TextBox controllo. Questo riferimento verrà usato per ottenere il valore immesso dall'utente per aggiornare l'entità associata alla proprietà di navigazione.

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

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

Utilizzerai l'evento GridView del controllo Updating per aggiornare la proprietà Location dell'entità associata OfficeAssignment. Aggiungere il gestore seguente per l'evento 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.
        }
    }
}

Questo codice viene eseguito quando l'utente fa clic su Aggiorna in una GridView riga. Il codice usa LINQ to Entities per recuperare l'entità OfficeAssignment associata all'entità corrente Person , usando l'oggetto PersonID della riga selezionata dall'argomento dell'evento.

Il codice esegue quindi una delle azioni seguenti a seconda del valore nel InstructorOfficeTextBox controllo :

  • Se la casella di testo ha un valore e non è presente alcuna OfficeAssignment entità da aggiornare, ne crea una.
  • Se la casella di testo ha un valore e c'è un'entità OfficeAssignment , aggiorna il valore della Location proprietà.
  • Se la casella di testo è vuota e un'entità OfficeAssignment esiste, elimina l'entità.

Successivamente, salva le modifiche apportate al database. Se si verifica un'eccezione, viene visualizzato un messaggio di errore.

Eseguire la pagina.

Immagine02

Fare clic su Modifica e tutti i campi cambiano in caselle di testo.

Immagine03

Modificare uno di questi valori, incluso l'assegnazione di Office. Fare clic su Aggiorna e verranno visualizzate le modifiche riportate nell'elenco.

Ogni insegnante può insegnare uno o più corsi, in modo da aggiungere un EntityDataSource controllo e un GridView controllo per elencare i corsi associati a qualsiasi insegnante sia selezionato nel controllo degli GridView insegnanti. Per creare un'intestazione e il EntityDataSource controllo per le entità dei corsi, aggiungere il markup seguente tra il controllo del messaggio Label di errore e il tag di chiusura </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>

Il Where parametro contiene il valore del PersonID dell'insegnante la cui riga è selezionata nel controllo InstructorsGridView. La Where proprietà contiene un comando di selezione secondaria che ottiene tutte le entità associate Person dalla proprietà di navigazione di Course un'entità People e seleziona l'entità Course solo se una delle entità associate Person contiene il valore selezionatoPersonID.

Per creare il controllo GridView, aggiungere il seguente markup immediatamente dopo il controllo CoursesEntityDataSource (prima del tag di chiusura </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>

Poiché non verrà visualizzato alcun corso se non è selezionato alcun insegnante, viene incluso un EmptyDataTemplate elemento.

Eseguire la pagina.

Image04

Selezionare un insegnante con uno o più corsi assegnati e il corso o i corsi vengono visualizzati nell'elenco. (Nota: anche se lo schema del database consente più corsi, nei dati di test forniti con il database nessun insegnante ha effettivamente più di un corso. È possibile aggiungere corsi al database manualmente usando la finestra Esplora server o la pagina CoursesAdd.aspx , che verrà aggiunta in un'esercitazione successiva.

Image05

Il CoursesGridView controllo mostra solo alcuni campi del corso. Per visualizzare tutti i dettagli di un corso, si userà un DetailsView controllo per il corso selezionato dall'utente. In Instructors.aspx aggiungere il markup seguente dopo il tag di chiusura </div> (assicurarsi di inserire questo markup dopo il tag div di chiusura, non prima):

<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>

Questo markup crea un EntityDataSource controllo associato al Courses set di entità. La Where proprietà seleziona un corso utilizzando il CourseID valore della riga selezionata nel controllo corsi GridView . Il markup specifica un gestore per l'evento Selected , che verrà usato in un secondo momento per visualizzare i voti degli studenti, che è un altro livello inferiore nella gerarchia.

In Instructors.aspx.cs creare lo stub seguente per il CourseDetailsEntityDataSource_Selected metodo . Questo stub verrà completato nel corso dell'esercitazione; per il momento è necessario affinché la pagina possa essere compilata ed eseguita.

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

Eseguire la pagina.

Image06

Inizialmente non ci sono dettagli del corso perché non è selezionato alcun corso. Selezionare un insegnante a cui è assegnato un corso e quindi selezionare un corso per visualizzare i dettagli.

Image07

Infine, vuoi mostrare tutti gli studenti iscritti e i loro voti per il corso selezionato. A tale scopo, si userà l'evento Selected del controllo EntityDataSource associato al corso DetailsView.

In Instructors.aspx aggiungere il markup seguente dopo il DetailsView controllo :

<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>

Questo markup crea un ListView controllo che visualizza un elenco di studenti e i voti per il corso selezionato. Non viene specificata alcuna origine dati perché eseguirai il databinding del controllo nel codice. L'elemento EmptyDataTemplate fornisce un messaggio da visualizzare quando non è selezionato alcun corso, in tal caso non ci sono studenti da visualizzare. L'elemento LayoutTemplate crea una tabella HTML per visualizzare l'elenco e ItemTemplate specifica le colonne da visualizzare. L'ID studente e il voto degli studenti provengono dall'entità StudentGrade e il nome dello studente proviene dall'entità Person resa disponibile da Entity Framework nella Person proprietà di navigazione dell'entità StudentGrade .

In Instructors.aspx.cs sostituire il metodo stubbed-out CourseDetailsEntityDataSource_Selected con il codice seguente:

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'argomento evento per questo evento fornisce i dati selezionati sotto forma di raccolta, che avrà zero elementi se non è selezionato alcun elemento o un elemento se è selezionata un'entità Course . Se è selezionata un'entità Course , il codice usa il First metodo per convertire la raccolta in un singolo oggetto. Ottiene quindi StudentGrade le entità dalla proprietà di navigazione, le converte in una raccolta e lega il controllo GradesListView alla raccolta.

Questo è sufficiente per visualizzare i voti, ma si vuole assicurarsi che il messaggio nel modello di dati vuoto venga visualizzato la prima volta che viene visualizzata la pagina e ogni volta che un corso non è selezionato. A tale scopo, creare il metodo seguente, che verrà chiamato da due posizioni:

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

Chiama questo nuovo metodo nel metodo Page_Load per visualizzare il template dei dati vuoto quando viene visualizzata la pagina per la prima volta. Chiamarlo dal InstructorsGridView_SelectedIndexChanged metodo perché l'evento viene generato quando viene selezionato un insegnante, il che significa che i nuovi corsi vengono caricati nel controllo dei corsi GridView e nessuno è ancora selezionato. Ecco le due chiamate:

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

Eseguire la pagina.

Image08

Selezionare un insegnante con un corso assegnato e quindi selezionare il corso.

Image09

Sono stati ora illustrati alcuni modi per lavorare con i dati correlati. Nell'esercitazione seguente si apprenderà come aggiungere relazioni tra entità esistenti, come rimuovere le relazioni e come aggiungere una nuova entità con una relazione a un'entità esistente.