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
Praca z powiązanymi danymi
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.
Wyświetlanie i aktualizowanie powiązanych jednostek w kontrolce GridView
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ę GridView
Label
, 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, aktualizujeLocation
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ę.
Kliknij pozycję Edytuj , a wszystkie pola zostaną zmienione na pola tekstowe.
Zmień dowolną z tych wartości, w tym przypisanie pakietu Office. Kliknij pozycję Aktualizuj , a zmiany zostaną odzwierciedlone na liście.
Wyświetlanie powiązanych jednostek w oddzielnej kontrolce
Każdy instruktor może uczyć co najmniej jeden kurs, więc dodasz kontrolę i kontrolkę EntityDataSource
GridView
, 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ę.
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.
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ę.
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.
Wyświetlanie powiązanych danych za pomocą zdarzenia EntityDataSource "Selected"
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ę.
Wybierz instruktora z przypisanym kursem, a następnie wybierz kurs.
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ą.