начало работы с базой данных 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 и выберите тот же отдел, чтобы просмотреть новый курс.

Image03

Работа со связями "многие ко многим"

Связь между набором сущностей 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();
}

Запустите страницу.

Снимок экрана: окно интернет-Обозреватель, в котором отображается представление

Выберите преподавателя. В раскрывающемся списке Назначить курсы отображаются курсы, которые не преподает преподаватель, а в раскрывающемся списке Удалить курс — курсы, которыми уже назначен преподаватель. В разделе Назначение курса выберите курс и нажмите кнопку Назначить. Курс переместится в раскрывающийся список Удалить курс . Выберите курс в разделе Удаление курса и нажмите кнопку Удалить. Курс переместится в раскрывающийся список Назначить курс .

Теперь вы ознакомились с несколькими способами работы со связанными данными. В следующем руководстве описано, как использовать наследование в модели данных для повышения удобства обслуживания приложения.