Entity Framework 4.0 Database First 및 ASP.NET 4 Web Forms 시작 - 4부

작성자 : Tom Dykstra

Contoso University 샘플 웹 애플리케이션은 Entity Framework 4.0 및 Visual Studio 2010을 사용하여 ASP.NET Web Forms 애플리케이션을 만드는 방법을 보여 줍니다. 자습서 시리즈에 대한 자세한 내용은 시리즈의 첫 번째 자습서를 참조하세요.

이전 자습서에서는 컨트롤을 EntityDataSource 사용하여 데이터를 필터링, 정렬 및 그룹화했습니다. 이 자습서에서는 관련 데이터를 표시하고 업데이트합니다.

강사 목록을 표시하는 강사 페이지를 만듭니다. 강사를 선택하면 해당 강사가 가르치는 과정 목록이 표시됩니다. 과정을 선택하면 과정에 대한 세부 정보와 과정에 등록된 학생 목록이 표시됩니다. 강사 이름, 채용 날짜 및 사무실 과제를 편집할 수 있습니다. 사무실 할당은 탐색 속성을 통해 액세스하는 별도의 엔터티 집합입니다.

master 데이터를 연결하여 태그 또는 코드에서 데이터를 자세히 설명할 수 있습니다. 자습서의 이 부분에서는 두 가지 방법을 모두 사용합니다.

Image01

Site.Master master 페이지를 사용하는 Instructors.aspx라는 새 웹 페이지를 만들고 라는 Content2컨트롤에 다음 태그를 Content 추가합니다.

<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> 태그 사이에 오류 메시지에 사용할 컨트롤과 컨트롤을 Label 만드는 GridView 다음 태그를 추가합니다.

<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 컨트롤은 행 선택을 사용하도록 설정하고, 밝은 회색 배경색으로 선택한 행을 강조 표시하며, 및 Updating 이벤트에 대한 처리기(나중에 만들 예정)를 SelectedIndexChanged 지정합니다. 또한 선택한 행의 키 값을 나중에 추가할 다른 컨트롤에 전달할 수 있도록 속성에 을 지정 PersonIDDataKeyNames 합니다.

마지막 열에는 연결된 엔터티에서 제공되므로 엔터티의 탐색 속성에 저장되는 강사의 Person 사무실 할당이 포함됩니다. 컨트롤을 EditItemTemplate 업데이트하기 위해 탐색 속성에 직접 바인딩할 수 없으므로 요소는 대신 BindGridView 를 지정 Eval 합니다. 코드에서 사무실 할당을 업데이트합니다. 이렇게 하려면 컨트롤에 대한 참조가 TextBox 필요하고 컨트롤의 Init 이벤트에 해당 컨트롤을 TextBox 가져와서 저장합니다.

다음 컨트롤 GridViewLabel 오류 메시지에 사용되는 컨트롤입니다. 컨트롤의 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;
}

컨트롤의 Updating 이벤트를 사용하여 GridView 연결된 OfficeAssignment 엔터티의 속성을 업데이트 Location 합니다. 이벤트에 대해 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 사용하여 이벤트 인수에서 선택한 행의 를 사용하여 PersonID 현재 Person 엔터티와 연결된 엔터티를 검색 OfficeAssignment 합니다.

그런 다음, 코드는 컨트롤의 값 InstructorOfficeTextBox 에 따라 다음 작업 중 하나를 수행합니다.

  • 텍스트 상자에 값이 있고 업데이트할 엔터티가 없 OfficeAssignment 으면 텍스트 상자가 만들어집니다.
  • 텍스트 상자에 값이 있고 엔터티가 OfficeAssignment 있는 경우 속성 값이 Location 업데이트됩니다.
  • 텍스트 상자가 비어 있고 OfficeAssignment 엔터티가 있으면 엔터티가 삭제됩니다.

그런 다음 변경 내용을 데이터베이스에 저장합니다. 예외가 발생하면 오류 메시지가 표시됩니다.

페이지를 실행합니다.

Image02

편집을 클릭하면 모든 필드가 텍스트 상자로 변경됩니다.

Image03

Office 할당을 포함하여 이러한 값을 변경합니다. 업데이트를 클릭하면 목록에 변경 내용이 반영됩니다.

각 강사는 하나 이상의 과정을 가르칠 수 있으므로 컨트롤과 컨트롤을 GridView 추가하여 EntityDataSource 강사 컨트롤에서 선택된 강사와 연결된 과정을 나열합니다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 엔터티의 People 탐색 속성에서 연결된 Person 모든 엔터티를 Course 가져오고 연결된 Person 엔터티 중 하나에 선택한 PersonID 값이 포함된 경우에만 엔터티를 선택하는 Course 하위 선택 명령이 포함되어 있습니다.

컨트롤을 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

하나 이상의 과정이 할당된 강사를 선택하면 과정 또는 과정이 목록에 표시됩니다. (참고: 데이터베이스 스키마는 여러 과정을 허용하지만 데이터베이스와 함께 제공되는 테스트 데이터에서는 강사가 실제로 둘 이상의 과정을 가지고 있지 않습니다. 서버 Explorer 창 또는 CoursesAdd.aspx 페이지를 사용하여 데이터베이스에 강좌를 직접 추가할 수 있습니다. 이 페이지는 이후 자습서에서 추가합니다.)

Image05

컨트롤에는 CoursesGridView 몇 가지 과정 필드만 표시됩니다. 과정에 대한 모든 세부 정보를 표시하려면 사용자가 선택한 과정에 대한 컨트롤을 사용합니다 DetailsView . Instructors.aspx에서 닫는 태그 다음에 다음 태그를 추가합니다(이 태그는 닫 </div> 는 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)
{
}

페이지를 실행합니다.

Image06

처음에는 강좌가 선택되지 않아 과정 세부 정보가 없습니다. 강좌가 할당된 강사를 선택한 다음, 과정을 선택하여 세부 정보를 확인합니다.

Image07

마지막으로, 등록된 모든 학생과 선택한 과정의 성적을 표시하려고 합니다. 이렇게 하려면 과정 DetailsViewSelected 바인딩된 컨트롤의 EntityDataSource 이벤트를 사용합니다.

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 표시할 열을 지정합니다. 학생 ID와 학생 성적은 엔터티에서 StudentGrade 가져오며, 학생 이름은 엔터티의 StudentGrade 탐색 속성에서 Person Entity Framework를 사용할 수 있도록 하는 엔터티에서 Person 가져옵니다.

Instructors.aspx.cs에서 스텁 아웃 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();
    }
}

이 이벤트에 대한 이벤트 인수는 선택한 데이터를 컬렉션 형식으로 제공하며, 아무 것도 선택하지 않으면 항목이 0개, 엔터티가 선택된 경우 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

이제 관련 데이터를 사용하는 몇 가지 방법을 보았습니다. 다음 자습서에서는 기존 엔터티 간의 관계를 추가하는 방법, 관계를 제거하는 방법 및 기존 엔터티와 관계가 있는 새 엔터티를 추가하는 방법을 알아봅니다.