Udostępnij za pośrednictwem


Wprowadzenie z programem Entity Framework 4.0 Database First i ASP.NET 4 Web Forms — część 4

Autor : Tom Dykstra

Przykładowa aplikacja internetowa Contoso University pokazuje, jak tworzyć aplikacje ASP.NET Web Forms przy użyciu platform Entity Framework 4.0 i Visual Studio 2010. Aby uzyskać informacje na temat serii samouczków, zobacz pierwszy samouczek z serii

W poprzednim samouczku użyto kontrolki do filtrowania EntityDataSource , sortowania i grupowania danych. W tym samouczku zostaną wyświetlone i zaktualizowane powiązane dane.

Utworzysz stronę Instruktorzy z listą instruktorów. Po wybraniu instruktora zostanie wyświetlona lista kursów nauczanych przez tego instruktora. Po wybraniu kursu zostaną wyświetlone szczegóły kursu i lista uczniów zarejestrowanych w kursie. Możesz edytować nazwę instruktora, datę zatrudnienia i przydział biura. Przypisanie pakietu Office to oddzielny zestaw jednostek, do którego uzyskujesz dostęp za pośrednictwem właściwości nawigacji.

Możesz połączyć dane główne z danymi szczegółowymi w adiustacji lub w kodzie. W tej części samouczka użyjesz obu metod.

Obraz01

Utwórz nową stronę internetową o nazwie Instructors.aspx korzystającą ze strony wzorcowej Site.Master i dodaj następujący znacznik do kontrolki Content o nazwie 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>

Ten znacznik tworzy kontrolkę EntityDataSource , która wybiera instruktorów i włącza aktualizacje. Element div konfiguruje znaczniki do renderowania po lewej stronie, aby można było dodać kolumnę po prawej stronie.

Między znacznikami EntityDataSource i tagiem zamykającym </div> dodaj następujący znacznik, który tworzy kontrolkę i kontrolkę GridViewLabel , która będzie używana dla komunikatów o błędach:

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

Ta GridView kontrolka umożliwia zaznaczanie wierszy, wyróżnia zaznaczony wiersz z jasnoszarym kolorem tła i określa programy obsługi (które zostaną utworzone później) dla zdarzeń SelectedIndexChanged i Updating . Określa również PersonID właściwość DataKeyNames , aby wartość klucza wybranego wiersza mogła zostać przekazana do innej kontrolki, którą dodasz później.

Ostatnia kolumna zawiera przypisanie biura instruktora, które jest przechowywane we właściwości Person nawigacji jednostki, ponieważ pochodzi z skojarzonej jednostki. Zwróć uwagę, że EditItemTemplate element określa Eval wartość zamiast Bind, ponieważ kontrolka GridView nie może bezpośrednio powiązać z właściwościami nawigacji, aby je zaktualizować. Zaktualizujesz przypisanie pakietu Office w kodzie. W tym celu będziesz potrzebować odwołania do kontrolki TextBox i uzyskasz i zapiszesz je w TextBox zdarzeniu kontrolki Init .

Po kontrolce GridView jest kontrolka Label używana do komunikatów o błędach. Właściwość kontrolki Visible to false, a stan widoku jest wyłączony, dzięki czemu etykieta będzie wyświetlana tylko wtedy, gdy kod będzie widoczny w odpowiedzi na błąd.

Otwórz plik Instructors.aspx.cs i dodaj następującą using instrukcję:

using ContosoUniversity.DAL;

Dodaj pole klasy prywatnej bezpośrednio po deklaracji nazwy częściowej klasy w celu przechowywania odwołania do pola tekstowego przypisania pakietu Office.

private TextBox instructorOfficeTextBox;

Dodaj wycinkę procedury SelectedIndexChanged obsługi zdarzeń, dla której dodasz kod do późniejszego użycia. Dodaj również procedurę obsługi dla zdarzenia kontrolki przypisania TextBox pakietu Office, aby można było przechowywać odwołanie do kontrolkiTextBox.Init Użyjesz tego odwołania, aby uzyskać wartość wprowadzoną przez użytkownika w celu zaktualizowania jednostki skojarzonej z właściwością nawigacji.

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

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

Użyjesz GridView zdarzenia kontrolki Updating , aby zaktualizować Location właściwość skojarzonej OfficeAssignment jednostki. Dodaj następującą procedurę obsługi dla zdarzenia 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.
        }
    }
}

Ten kod jest uruchamiany, gdy użytkownik kliknie pozycję Aktualizuj w GridView wierszu. Kod używa LINQ to Entities do pobrania OfficeAssignment jednostki skojarzonej z bieżącą Person jednostką przy użyciu PersonID wiersza wybranego z argumentu zdarzenia.

Następnie kod wykonuje jedną z następujących akcji w zależności od wartości w kontrolce InstructorOfficeTextBox :

  • Jeśli pole tekstowe ma wartość i nie ma OfficeAssignment jednostki do zaktualizowania, tworzy ją.
  • Jeśli pole tekstowe ma wartość i istnieje OfficeAssignment jednostka, aktualizuje Location wartość właściwości.
  • Jeśli pole tekstowe jest puste i OfficeAssignment istnieje jednostka, spowoduje to usunięcie jednostki.

Następnie zapisuje zmiany w bazie danych. Jeśli wystąpi wyjątek, zostanie wyświetlony komunikat o błędzie.

Uruchom stronę.

Obraz02

Kliknij pozycję Edytuj , a wszystkie pola zostaną zmienione na pola tekstowe.

Obraz03

Zmień dowolną z tych wartości, w tym przypisanie pakietu Office. Kliknij pozycję Aktualizuj , a zmiany zostaną odzwierciedlone na liście.

Każdy instruktor może uczyć co najmniej jeden kurs, więc dodasz kontrolę i kontrolkę EntityDataSourceGridView , aby wyświetlić listę kursów związanych z wybranym instruktorem w kontrolce instruktorów GridView . Aby utworzyć nagłówek i kontrolkę EntityDataSource dla jednostek kursów, dodaj następujące znaczniki między kontrolką komunikatu Label o błędzie a tagiem zamykającym </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>

Parametr Where zawiera wartość PersonID instruktora, którego wiersz jest zaznaczony w kontrolce InstructorsGridView . Właściwość Where zawiera polecenie wyboru podrzędnego, które pobiera wszystkie skojarzone Person jednostki z Course właściwości nawigacji jednostki People i wybiera Course jednostkę tylko wtedy, gdy jedna ze skojarzonych Person jednostek zawiera wybraną PersonID wartość.

Aby utworzyć kontrolkę GridView , dodaj następujący znacznik bezpośrednio po kontrolce CoursesEntityDataSource (przed tagiem zamykającym </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>

Ponieważ żadne kursy nie będą wyświetlane, jeśli nie wybrano żadnego instruktora EmptyDataTemplate , element zostanie uwzględniony.

Uruchom stronę.

Obraz04

Wybierz instruktora, który ma przypisane co najmniej jedno kursy, a kurs lub kursy są wyświetlane na liście. (Uwaga: mimo że schemat bazy danych zezwala na wiele kursów, w danych testowych dostarczonych z bazą danych żaden instruktor w rzeczywistości nie ma więcej niż jednego kursu. Kursy można dodawać do bazy danych samodzielnie przy użyciu okna Eksploratora serwera lub strony CoursesAdd.aspx , którą dodasz w kolejnym samouczku.

Obraz05

Kontrolka CoursesGridView pokazuje tylko kilka pól kursu. Aby wyświetlić wszystkie szczegóły kursu, użyjesz DetailsView kontrolki dla kursu wybranego przez użytkownika. W pliku Instructors.aspx dodaj następujący znacznik po tagu zamykającym </div> (pamiętaj, aby umieścić ten znacznik po zamykającym tagu div, a nie przed nim):

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

Ten znacznik tworzy kontrolkę powiązaną EntityDataSource z zestawem Courses jednostek. Właściwość Where wybiera kurs przy użyciu CourseID wartości wybranego wiersza w kontrolce kursów GridView . Znacznik określa procedurę obsługi zdarzenia Selected , która będzie używana później do wyświetlania ocen uczniów, co jest kolejnym poziomem niższym w hierarchii.

W pliku Instructors.aspx.cs utwórz następujący wycink dla CourseDetailsEntityDataSource_Selected metody . (Ten wycinkę wypełnisz w dalszej części samouczka. Na razie będzie on potrzebny, aby strona została skompilowana i uruchomiona).

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

Uruchom stronę.

Obraz06

Początkowo nie ma żadnych szczegółów kursu, ponieważ nie wybrano kursu. Wybierz instruktora, któremu przypisano kurs, a następnie wybierz kurs, aby wyświetlić szczegóły.

Obraz07

Na koniec chcesz pokazać wszystkich zarejestrowanych uczniów i ich oceny dla wybranego kursu. W tym celu użyjesz Selected zdarzenia kontrolki powiązanej EntityDataSource z kursem DetailsView.

W pliku Instructors.aspx dodaj następujący znacznik po kontrolce DetailsView :

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

Ten znacznik tworzy kontrolkę ListView , która wyświetla listę uczniów i ich ocen dla wybranego kursu. Nie określono źródła danych, ponieważ powiązanie kontrolki w kodzie będzie wiązane. Element EmptyDataTemplate zawiera komunikat do wyświetlenia, gdy nie wybrano żadnego kursu — w takim przypadku nie ma uczniów do wyświetlenia. Element LayoutTemplate tworzy tabelę HTML w celu wyświetlenia listy i ItemTemplate określa kolumny do wyświetlenia. Identyfikator ucznia i ocena ucznia pochodzą z StudentGrade jednostki, a nazwa ucznia pochodzi z Person jednostki, którą platforma Entity Framework udostępnia we Person właściwości StudentGrade nawigacji jednostki.

W pliku Instructors.aspx.cs zastąp metodę stubbed-out CourseDetailsEntityDataSource_Selected następującym kodem:

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();
    }
}

Argument zdarzenia dla tego zdarzenia zawiera wybrane dane w postaci kolekcji, które będą miały zero elementów, jeśli nic nie zostanie wybrane lub jeden element, jeśli Course wybrano jednostkę. W przypadku wybrania Course jednostki kod używa First metody , aby przekonwertować kolekcję na pojedynczy obiekt. Następnie pobiera StudentGrade jednostki z właściwości nawigacji, konwertuje GradesListView je na kolekcję i wiąże kontrolkę z kolekcją.

Jest to wystarczające do wyświetlania ocen, ale chcesz upewnić się, że komunikat w pustym szablonie danych jest wyświetlany po raz pierwszy na stronie i za każdym razem, gdy kurs nie jest wybrany. W tym celu utwórz następującą metodę, którą wywołasz z dwóch miejsc:

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

Wywołaj tę nową metodę z Page_Load metody , aby wyświetlić pusty szablon danych przy pierwszym wyświetleniu strony. I nazwij InstructorsGridView_SelectedIndexChanged to z metody, ponieważ to zdarzenie jest zgłaszane po wybraniu instruktora, co oznacza, że nowe kursy są ładowane do kontroli kursów GridView i żaden nie został jeszcze wybrany. Oto dwa wywołania:

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

Uruchom stronę.

Obraz08

Wybierz instruktora z przypisanym kursem, a następnie wybierz kurs.

Obraz09

Teraz przedstawiono kilka sposobów pracy z powiązanymi danymi. W poniższym samouczku dowiesz się, jak dodawać relacje między istniejącymi jednostkami, jak usuwać relacje i jak dodać nową jednostkę, która ma relację z istniejącą jednostką.