Поделиться через


начало работы с entity Framework 4.0 Database First и ASP.NET 4 веб-формы — часть 4

Том Дайкстра

Пример веб-приложения Contoso University демонстрирует создание ASP.NET Web Forms приложений с помощью Entity Framework 4.0 и Visual Studio 2010. Сведения о серии учебников см. в первом руководстве серии

В предыдущем руководстве EntityDataSource вы использовали элемент управления для фильтрации, сортировки и группировки данных. В этом руководстве вы будете отображать и обновлять связанные данные.

Вы создадите страницу Преподаватели со списком преподавателей. При выборе преподавателя отображается список курсов, преподаваемых этим инструктором. При выборе курса вы увидите сведения о курсе и список учащихся, зачисленных на курс. Вы можете изменить имя преподавателя, дату найма и назначение в офисе. Назначение office — это отдельный набор сущностей, доступ к которому осуществляется через свойство навигации.

Вы можете связать данные master с подробными данными в разметке или коде. В этой части учебника вы будете использовать оба метода.

Изображение01

Создайте новую веб-страницу с именем Instructors.aspx, которая использует страницу master 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 элемент управления не может напрямую привязаться к свойствам навигации для их обновления. Вы обновите назначение office в коде. Для этого вам потребуется ссылка на TextBox элемент управления, а затем вы получите и сохраните ее в событии TextBoxInit элемента управления.

GridView За элементом Label управления следует элемент управления, который используется для сообщений об ошибках. Свойство элемента управления Visible имеет значение false, а состояние представления отключено, поэтому метка будет отображаться только в том случае, если код делает ее видимой в ответ на ошибку.

Откройте файл Instructors.aspx.cs и добавьте следующую using инструкцию:

using ContosoUniversity.DAL;

Добавьте поле частного класса сразу после объявления имени разделяемого класса, чтобы содержать ссылку на текстовое поле назначения office.

private TextBox instructorOfficeTextBox;

Добавьте заглушку для обработчика SelectedIndexChanged событий, которую вы добавите в дальнейшем. Кроме того, добавьте обработчик для события элемента управления Init назначением TextBox office, чтобы можно было сохранить ссылку TextBox на элемент управления. Эта ссылка используется для получения значения, введенного пользователем, чтобы обновить сущность, связанную со свойством навигации.

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

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

Событие элемента управления Updating используется 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 Entities для получения сущностиOfficeAssignment, связанной с текущей Person сущностью, с помощью PersonID выбранной строки из аргумента event.

Затем код выполняет одно из следующих действий в зависимости от значения в элементе InstructorOfficeTextBox управления :

  • Если текстовое поле содержит значение и сущность для обновления отсутствует OfficeAssignment , оно создает его.
  • Если текстовое поле содержит значение и есть OfficeAssignment сущность, оно обновляет Location значение свойства.
  • Если текстовое поле пусто и OfficeAssignment сущность существует, сущность удаляется.

После этого изменения сохраняются в базе данных. При возникновении исключения отображается сообщение об ошибке.

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

Image02

Щелкните Изменить , и все поля будут изменены на текстовые поля.

Image03

Измените любое из этих значений, включая назначение 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 свойства навигации сущности PeopleCourse и выбирает сущность, только если одна из связанных 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 включается элемент .

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

Image04

Выберите преподавателя, которому назначен один или несколько курсов, и этот курс или курсы появятся в списке. (Примечание. Несмотря на то, что схема базы данных допускает несколько курсов, в тестовых данных, поставляемых с базой данных, ни один преподаватель на самом деле не имеет более одного курса. Вы можете самостоятельно добавлять курсы в базу данных с помощью окна Обозреватель сервера или страницы CoursesAdd.aspx, которые вы добавите в следующем руководстве.)

Image05

Элемент 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 значение выбранной строки в элементе управления Courses GridView . Разметка задает обработчик для Selected события, который будет использоваться позже для отображения оценок учащихся, что является еще одним уровнем ниже в иерархии.

В файле Instructors.aspx.cs создайте следующую заглушку для CourseDetailsEntityDataSource_Selected метода . (Вы заполните эту заглушку позже в руководстве. Пока она вам понадобится, чтобы страница скомпилирована и запущена.)

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

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

Image06

Изначально нет сведений о курсе, так как курс не выбран. Выберите преподавателя, которому назначен курс, а затем выберите курс, чтобы просмотреть подробные сведения.

Изображение07

Наконец, необходимо отобразить всех зачисленных учащихся и их оценки для выбранного курса. Для этого используется 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();
}

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

Image08

Выберите преподавателя, которому назначен курс, а затем выберите курс.

Image09

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