Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Пример веб-приложения Contoso University демонстрирует создание приложений веб-форм ASP.NET с помощью Entity Framework 4.0 и Visual Studio 2010. Сведения о серии учебников см. в первом руководстве в серии
Работа с связанными данными
В предыдущем руководстве вы использовали EntityDataSource элемент управления для фильтрации, сортировки и группирования данных. В этом руководстве вы отобразите и обновите связанные данные.
Вы создадите страницу "Преподаватели", которая содержит список инструкторов. При выборе инструктора вы увидите список курсов, преподаваемых этим инструктором. При выборе курса вы увидите подробные сведения о курсе и список студентов, зарегистрированных в курсе. Вы можете изменить имя инструктора, дату найма и назначение офиса. Назначение офиса — это отдельный набор объектов, к которым вы обращаетесь через свойство навигации.
Основные данные можно связать с подробными данными в разметке или коде. В этой части руководства вы будете использовать оба метода.
Отображение и обновление связанных сущностей в элементе управления GridView
Создайте новую веб-страницу с именем Instructors.aspx , которая использует главную страницу Site.Master , и добавьте следующую разметку в Content элемент управления с именем 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>
Эта разметка создает EntityDataSource элемент управления, который выбирает инструкторов и включает обновления. Элемент div настраивает разметку для отображения слева, что позволяет позже добавить столбец справа.
Между разметкой EntityDataSource и закрывающим тегом </div> добавьте следующую разметку, создающую элементы управления GridView и Label, которые вы будете использовать для сообщений об ошибках.
<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>
Этот GridView элемент управления включает выделение строк, выделяет выбранную строку с светло-серым цветом фона и задает обработчики (которые будут созданы позже) для SelectedIndexChanged событий и Updating событий. Он также указывает PersonID для DataKeyNames свойства, чтобы ключевое значение выбранной строки можно было передать другому элементу управления, который вы добавите позже.
Последний столбец содержит информацию о кабинете инструктора, которая хранится в навигационном свойстве сущности Person, поскольку она поступает из связанной сущности. Обратите внимание, что EditItemTemplate элемент указывает Eval вместо Bindэлемента, так как GridView элемент управления не может напрямую привязаться к свойствам навигации, чтобы обновить их. Вы обновите назначение офиса в коде. Для этого вам потребуется ссылка на TextBox элемент управления, и вы получите и сохраните его в TextBox событии Init элемента управления.
После элемента управления GridView используется элемент управления Label, предназначенный для сообщений об ошибках. Свойство Visible элемента управления имеет значение false, и состояние представления отключено, так что метка будет отображаться только в том случае, если код сделает её видимой в ответ на ошибку.
Откройте файл Instructors.aspx.cs и добавьте следующую using инструкцию:
using ContosoUniversity.DAL;
Добавьте частное поле класса сразу после объявления имени частичного класса, чтобы сохранить ссылку на текстовое поле назначения офиса.
private TextBox instructorOfficeTextBox;
Добавьте заглушку для обработчика события SelectedIndexChanged, куда вы позже добавите код. Кроме того, добавьте обработчик для события элемента управления TextBox назначения офиса Init, чтобы сохранить ссылку на этот элемент TextBox управления. Эта ссылка будет использоваться для получения значения, введенного пользователем, для обновления сущности, связанной со свойством навигации.
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
}
protected void InstructorOfficeTextBox_Init(object sender, EventArgs e)
{
instructorOfficeTextBox = sender as TextBox;
}
Вы будете использовать событие элемента управления GridView для обновления свойства Location связанной сущности OfficeAssignment. Добавьте следующий обработчик для 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.
}
}
}
Этот код выполняется, когда пользователь нажимает кнопку "Обновить " в строке GridView . Код использует LINQ to Entity для получения OfficeAssignment сущности, связанной с текущей Person сущностью, с помощью PersonID выбранной строки из аргумента события.
Затем код принимает одно из следующих действий в зависимости от значения в элементе InstructorOfficeTextBox управления:
- Если текстовое поле имеет значение и отсутствует сущность
OfficeAssignmentдля обновления, то она создается. - Если в текстовом поле есть значение и обнаружена
OfficeAssignmentсущность, она обновляет значение свойстваLocation. - Если текстовое поле пусто, а
OfficeAssignmentсущность существует, она удаляет сущность.
После этого он сохраняет изменения в базе данных. При возникновении исключения отображается сообщение об ошибке.
Запустите страницу.
Нажмите кнопку "Изменить" и все поля изменяются на текстовые поля.
Измените любое из этих значений, включая назначение Office. Нажмите кнопку "Обновить" , и вы увидите изменения, отраженные в списке.
Отображение связанных сущностей в отдельном элементе управления
Каждый инструктор может обучать один или несколько курсов, поэтому вы добавите EntityDataSource элемент управления и GridView элемент управления для перечисления курсов, связанных с любым инструктором, выбранным в элементе управления инструкторами GridView . Чтобы создать заголовок и элемент управления EntityDataSource для сущностей курсов, вставьте следующую разметку между элементом управления сообщением об ошибке Label и закрывающим тегом </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>
Параметр Where содержит значение PersonID инструктора, чья строка выбрана в управляющем элементе InstructorsGridView. Свойство Where содержит команду subselect, которая получает все связанные Person сущности из Course свойства навигации сущности People и выбирает Course сущность, только если одна из связанных Person сущностей содержит выбранное PersonID значение.
Чтобы создать GridView элемент управления., добавьте следующую разметку сразу после CoursesEntityDataSource элемента управления (перед закрывающим </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>
Так как курсы не будут отображаться, если не выбран инструктор, EmptyDataTemplate элемент включен.
Запустите страницу.
Выберите инструктора, которому назначены один или несколько курсов, и эти курсы отображаются в списке. (Примечание. Хотя схема базы данных разрешает несколько курсов, в тестовых данных, предоставленных базой данных, инструктор фактически не имеет более одного курса. Вы можете добавлять курсы в базу данных самостоятельно с помощью окна обозревателя серверов или страницы CoursesAdd.aspx , которую вы добавите в более позднем руководстве.)
В элементе CoursesGridView управления отображаются только несколько полей курса. Чтобы отобразить все сведения для курса, вы будете использовать DetailsView элемент управления для выбранного пользователем курса. В Instructors.aspx добавьте следующую разметку после закрывающего тега (убедитесь, что вы помещаете эту разметку </div> закрывающего тега div, а не перед ним):
<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>
Эта разметка создает элемент управления EntityDataSource, который привязан к набору сущностей Courses. Свойство Where выбирает курс, используя CourseID значение выбранной строки в элементе управления "Курсы GridView ". Разметка задает обработчик события Selected, который вы будете использовать позже для отображения оценок учащихся, что находится на уровне ниже в иерархии.
В Instructors.aspx.cs создайте следующую заглушку для CourseDetailsEntityDataSource_Selected метода. (Вы заполните этот временный фрагмент позже в учебнике; пока он нужен, чтобы страница компилировалась и выполнялась.)
protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}
Запустите страницу.
Изначально нет сведений о курсе, так как курс не выбран. Выберите инструктора, которому назначен курс, а затем выберите курс, чтобы просмотреть сведения.
Использование события EntityDataSource "Selected" для отображения связанных данных
Наконец, вы хотите показать все зарегистрированные учащиеся и их оценки для выбранного курса. Для этого вы будете использовать событие Selected элемента управления EntityDataSource, привязанного к курсу DetailsView.
В Instructors.aspx добавьте следующую разметку после 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>
Эта разметка создает ListView элемент управления, который отображает список учащихся и их оценки для выбранного курса. Источник данных не указан, поскольку вы будете связывать элемент управления с данными в коде. Элемент EmptyDataTemplate предоставляет сообщение для отображения, если курс не выбран — в этом случае нет учащихся для отображения. Элемент LayoutTemplate создает html-таблицу для отображения списка и ItemTemplate указывает столбцы для отображения. Идентификатор учащегося и оценка учащегося относятся к StudentGrade сущности, а имя учащегося - от Person сущности, которую Entity Framework делает доступной в навигационном свойстве Person сущности StudentGrade.
В Instructors.aspx.cs замените метод stubbed-out CourseDetailsEntityDataSource_Selected следующим кодом:
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();
}
}
Аргумент события для этого события предоставляет выбранные данные в виде коллекции, которая будет иметь нулевые элементы, если ничего не выбрано или один элемент, если выбрана Course сущность.
Course Если выбрана сущность, код использует First метод для преобразования коллекции в один объект. Затем он получает StudentGrade сущности из свойства навигации, преобразует их в коллекцию и привязывает GradesListView элемент управления к коллекции.
Это достаточно для отображения оценок, но вы хотите убедиться, что сообщение в пустом шаблоне данных отображается при первом отображении страницы и всякий раз, когда курс не выбран. Для этого создайте следующий метод, который будет вызываться из двух мест:
private void ClearStudentGradesDataSource()
{
var emptyStudentGradesList = new List<StudentGrade>();
GradesListView.DataSource = emptyStudentGradesList;
GradesListView.DataBind();
}
Вызовите этот новый метод из Page_Load метода, чтобы отобразить пустой шаблон данных при первом отображении страницы. И вызовите его из InstructorsGridView_SelectedIndexChanged метода, так как это событие возникает при выборе инструктора, что означает, что новые курсы загружаются в элемент управления курсами GridView , и никто еще не выбран. Ниже приведены два вызова:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ClearStudentGradesDataSource();
}
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
ClearStudentGradesDataSource();
}
Запустите страницу.
Выберите инструктора, которому назначен курс, а затем выберите курс.
Теперь вы видели несколько способов работы с связанными данными. В следующем руководстве вы узнаете, как добавить связи между существующими сущностями, как удалить связи и как добавить новую сущность, которая имеет отношение к существующей сущности.