начало работы с базой данных Entity Framework 4.0 и ASP.NET 4 веб-формы— часть 5
Пример веб-приложения Contoso University демонстрирует создание ASP.NET Web Forms приложений с помощью Entity Framework 4.0 и Visual Studio 2010. Сведения о серии учебников см. в первом руководстве серии
Продолжение работы со связанными данными
В предыдущем руководстве вы начали использовать элемент управления для работы со связанными EntityDataSource
данными. Вы отображали несколько уровней иерархии и измененные данные в свойствах навигации. В этом руководстве вы продолжите работать со связанными данными, добавляя и удаляя связи, а также добавляя новую сущность, которая имеет связь с существующей сущностью.
Вы создадите страницу, на которую добавляются курсы, назначенные отделам. Отделы уже существуют, и при создании нового курса вы в то же время устанавливаете связь между ним и существующим отделом.
Вы также создадите страницу, которая работает с отношением "многие ко многим", назначив преподавателя курсу (добавив связь между двумя выбранными сущностями) или удалив преподавателя из курса (удалив связь между двумя выбранными сущностями). В базе данных добавление связи между преподавателем и курсом приводит к добавлению новой строки в таблицу CourseInstructor
сопоставлений. Удаление связи включает удаление строки из таблицы сопоставлений CourseInstructor
. Однако это можно сделать в Entity Framework, задав свойства навигации без явной ссылки на таблицу CourseInstructor
.
Добавление сущности с отношением к существующей сущности
Создайте новую веб-страницу с именем CoursesAdd.aspx, которая использует страницу master Site.Master, и добавьте следующую разметку в Content
элемент управления с именем Content2
:
<h2>Add Courses</h2>
<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="Courses"
EnableInsert="True" EnableDelete="True" >
</asp:EntityDataSource>
<asp:DetailsView ID="CoursesDetailsView" runat="server" AutoGenerateRows="False"
DataSourceID="CoursesEntityDataSource" DataKeyNames="CourseID"
DefaultMode="Insert" oniteminserting="CoursesDetailsView_ItemInserting">
<Fields>
<asp:BoundField DataField="CourseID" HeaderText="ID" />
<asp:BoundField DataField="Title" HeaderText="Title" />
<asp:BoundField DataField="Credits" HeaderText="Credits" />
<asp:TemplateField HeaderText="Department">
<InsertItemTemplate>
<asp:EntityDataSource ID="DepartmentsEntityDataSource" runat="server" ConnectionString="name=SchoolEntities"
DefaultContainerName="SchoolEntities" EnableDelete="True" EnableFlattening="False"
EntitySetName="Departments" EntityTypeFilter="Department">
</asp:EntityDataSource>
<asp:DropDownList ID="DepartmentsDropDownList" runat="server" DataSourceID="DepartmentsEntityDataSource"
DataTextField="Name" DataValueField="DepartmentID"
oninit="DepartmentsDropDownList_Init">
</asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
Эта разметка создает элемент EntityDataSource
управления, который выбирает курсы, который включает вставку и задает обработчик для Inserting
события. Обработчик будет использоваться для обновления свойства навигации Department
при создании новой Course
сущности.
Разметка также создает DetailsView
элемент управления для добавления новых Course
сущностей. Разметка использует привязанные поля для Course
свойств сущности. Необходимо ввести значение, CourseID
так как это не поле идентификатора, созданное системой. Вместо этого это номер курса, который необходимо указать вручную при создании курса.
Для свойства навигации Department
используется поле шаблона, так как свойства навигации нельзя использовать с BoundField
элементами управления. Поле шаблона содержит раскрывающийся список для выбора отдела. Раскрывающийся список привязан к набору Departments
сущностей с помощью , Eval
а не Bind
, так как невозможно напрямую привязать свойства навигации для их обновления. Вы указываете обработчик для DropDownList
события элемента управления Init
, чтобы можно было сохранить ссылку на элемент управления для использования кодом, обновляющим внешний DepartmentID
ключ.
В Файле CoursesAdd.aspx.cs сразу после объявления разделяемого класса добавьте поле класса для хранения ссылки на DepartmentsDropDownList
элемент управления :
private DropDownList departmentDropDownList;
Добавьте обработчик для DepartmentsDropDownList
события элемента управления Init
, чтобы можно было сохранить ссылку на элемент управления. Это позволяет получить значение, введенное пользователем, и использовать его для обновления DepartmentID
значения сущности Course
.
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
departmentDropDownList = sender as DropDownList;
}
Добавьте обработчик для DetailsView
события элемента управления Inserting
:
protected void CoursesDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
var departmentID = Convert.ToInt32(departmentDropDownList.SelectedValue);
e.Values["DepartmentID"] = departmentID;
}
Когда пользователь щелкает Insert
, Inserting
событие возникает перед вставленной новой записью. Код в обработчике получает DepartmentID
из DropDownList
элемента управления и использует его для задания значения, которое будет использоваться для DepartmentID
свойства сущности Course
.
Entity Framework позаботится о добавлении этого курса в свойство навигации Courses
связанной Department
сущности. Он также добавляет отдел в свойство навигации Department
сущности Course
.
Запустите страницу.
Введите идентификатор, заголовок, количество кредитов и выберите отдел, а затем нажмите кнопку Вставить.
Запустите страницу Courses.aspx и выберите тот же отдел, чтобы просмотреть новый курс.
Работа со связями "многие ко многим"
Связь между набором сущностей Courses
и набором сущностей People
является связью "многие ко многим". Сущность Course
имеет свойство навигации с именем People
, которое может содержать ноль, одну или несколько связанных Person
сущностей (представляющих инструкторов, назначенных для преподавания этого курса). Person
Сущность имеет свойство навигации с именем Courses
, которое может содержать ноль, одну или несколько связанных Course
сущностей (представляющих курсы, которые преподавателю назначено преподавать). Один преподаватель может преподавать несколько курсов, и один курс может преподаваться несколькими преподавателями. В этом разделе пошагового руководства вы добавите и удалите связи между Person
сущностями и Course
, обновив свойства навигации связанных сущностей.
Создайте новую веб-страницу с именем InstructorsCourses.aspx, которая использует страницу master Site.Master, и добавьте следующую разметку в Content
элемент управления с именем Content2
:
<h2>Assign Instructors to Courses or Remove from Courses</h2>
<br />
<asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="People"
Where="it.HireDate is not null" Select="it.LastName + ', ' + it.FirstMidName AS Name, it.PersonID">
</asp:EntityDataSource>
Select an Instructor:
<asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsEntityDataSource"
AutoPostBack="true" DataTextField="Name" DataValueField="PersonID"
OnSelectedIndexChanged="InstructorsDropDownList_SelectedIndexChanged"
OnDataBound="InstructorsDropDownList_DataBound">
</asp:DropDownList>
<h3>
Assign a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="UnassignedCoursesDropDownList" runat="server"
DataTextField="Title" DataValueField="CourseID">
</asp:DropDownList>
<br />
<asp:Button ID="AssignCourseButton" runat="server" Text="Assign" OnClick="AssignCourseButton_Click" />
<br />
<asp:Label ID="CourseAssignedLabel" runat="server" Visible="false" Text="Assignment successful"></asp:Label>
<br />
<h3>
Remove a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="AssignedCoursesDropDownList" runat="server"
DataTextField="title" DataValueField="courseiD">
</asp:DropDownList>
<br />
<asp:Button ID="RemoveCourseButton" runat="server" Text="Remove" OnClick="RemoveCourseButton_Click" />
<br />
<asp:Label ID="CourseRemovedLabel" runat="server" Visible="false" Text="Removal successful"></asp:Label>
Эта разметка создает элемент EntityDataSource
управления, который получает имена и PersonID
сущности Person
для преподавателей. Элемент DropDrownList
управления привязан к элементу EntityDataSource
управления . Элемент DropDownList
управления задает обработчик для DataBound
события. Этот обработчик будет использоваться для привязки данных к двум раскрывающимся спискам, в котором отображаются курсы.
Разметка также создает следующую группу элементов управления для назначения курса выбранному преподавателю:
- Элемент
DropDownList
управления для выбора курса для назначения. Этот элемент управления будет заполнен курсами, которые в настоящее время не назначены выбранному преподавателю. - Элемент
Button
управления для инициации назначения. - Элемент
Label
управления для отображения сообщения об ошибке в случае сбоя назначения.
Наконец, разметка также создает группу элементов управления для удаления курса из выбранного преподавателя.
В Файле InstructorsCourses.aspx.cs добавьте оператор using:
using ContosoUniversity.DAL;
Добавьте метод для заполнения двух раскрывающихся списков, в котором отображаются курсы:
private void PopulateDropDownLists()
{
using (var context = new SchoolEntities())
{
var allCourses = (from c in context.Courses
select c).ToList();
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People.Include("Courses")
where p.PersonID == instructorID
select p).First();
var assignedCourses = instructor.Courses.ToList();
var unassignedCourses = allCourses.Except(assignedCourses.AsEnumerable()).ToList();
UnassignedCoursesDropDownList.DataSource = unassignedCourses;
UnassignedCoursesDropDownList.DataBind();
UnassignedCoursesDropDownList.Visible = true;
AssignedCoursesDropDownList.DataSource = assignedCourses;
AssignedCoursesDropDownList.DataBind();
AssignedCoursesDropDownList.Visible = true;
}
}
Этот код получает все курсы из Courses
набора сущностей и курсы из Courses
свойства навигации сущности Person
для выбранного преподавателя. Затем он определяет, какие курсы назначены данному преподавателю, и заполняет раскрывающиеся списки соответствующим образом.
Добавьте обработчик для Assign
события кнопки Click
:
protected void AssignCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(UnassignedCoursesDropDownList.SelectedValue);
var course = (from c in context.Courses
where c.CourseID == courseID
select c).First();
instructor.Courses.Add(course);
try
{
context.SaveChanges();
PopulateDropDownLists();
CourseAssignedLabel.Text = "Assignment successful.";
}
catch (Exception)
{
CourseAssignedLabel.Text = "Assignment unsuccessful.";
//Add code to log the error.
}
CourseAssignedLabel.Visible = true;
}
}
Этот код получает Person
сущность для выбранного преподавателя, получает Course
сущность для выбранного курса и добавляет выбранный курс в Courses
свойство навигации сущности преподавателя Person
. Затем он сохраняет изменения в базе данных и повторно выполняет повторение раскрывающихся списков, чтобы результаты можно было увидеть немедленно.
Добавьте обработчик для Remove
события кнопки Click
:
protected void RemoveCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(AssignedCoursesDropDownList.SelectedValue);
var courses = instructor.Courses;
var courseToRemove = new Course();
foreach (Course c in courses)
{
if (c.CourseID == courseID)
{
courseToRemove = c;
break;
}
}
try
{
courses.Remove(courseToRemove);
context.SaveChanges();
PopulateDropDownLists();
CourseRemovedLabel.Text = "Removal successful.";
}
catch (Exception)
{
CourseRemovedLabel.Text = "Removal unsuccessful.";
//Add code to log the error.
}
CourseRemovedLabel.Visible = true;
}
}
Этот код получает Person
сущность для выбранного преподавателя, получает Course
сущность для выбранного курса и удаляет выбранный курс из Person
свойства навигации сущности Courses
. Затем он сохраняет изменения в базе данных и повторно выполняет повторение раскрывающихся списков, чтобы результаты можно было увидеть немедленно.
Добавьте в метод код Page_Load
, который гарантирует, что сообщения об ошибках не отображаются, если нет ошибок, и добавьте обработчики для DataBound
событий и SelectedIndexChanged
раскрывающегося списка преподавателей для заполнения раскрывающихся списков курсов:
protected void Page_Load(object sender, EventArgs e)
{
CourseAssignedLabel.Visible = false;
CourseRemovedLabel.Visible = false;
}
protected void InstructorsDropDownList_DataBound(object sender, EventArgs e)
{
PopulateDropDownLists();
}
protected void InstructorsDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
PopulateDropDownLists();
}
Запустите страницу.
Выберите преподавателя. В раскрывающемся списке Назначить курсы отображаются курсы, которые не преподает преподаватель, а в раскрывающемся списке Удалить курс — курсы, которыми уже назначен преподаватель. В разделе Назначение курса выберите курс и нажмите кнопку Назначить. Курс переместится в раскрывающийся список Удалить курс . Выберите курс в разделе Удаление курса и нажмите кнопку Удалить. Курс переместится в раскрывающийся список Назначить курс .
Теперь вы ознакомились с несколькими способами работы со связанными данными. В следующем руководстве описано, как использовать наследование в модели данных для повышения удобства обслуживания приложения.