Walkthrough: Mapping Inheritance - Table-per-Type (Entity Data Model Tools)

This topic shows how to implement table-per-type inheritance by modifying a conceptual model. Table-per-type inheritance uses a separate table in the database to maintain data for non-inherited properties and key properties for each type in the inheritance hierarchy.

In this walkthrough you will implement table-per-type inheritance by modifying the conceptual model used in the CourseManager application (for more information, see the Prerequisites section, later in this topic).

In the CourseManager application, the Course, OnlineCourse, and OnsiteCourse entity types map to tables of the same names. Because the OnlineCourse and OnsiteCourse entity types contain information that is unique to the two course types and they share a common key, they can be changed to inherit from the Course entity type. The following steps summarize how to implement table-per-type inheritance in this case (the procedures later in this topic provide more detail):

  1. Delete the association between the OnlineCourse entity type and the Course entity type. Delete the association between the OnsiteCourse entity type and the Course entity type. These associations will be replaced by implementing the inheritance hierarchy.

  2. Delete the CourseID key property from the OnlineCourse and OnsiteCourse entity types. This property will be inherited from the Course entity type.

  3. Set the Course entity type as the base type for the OnlineCourse and OnsiteCourse entity types.

  4. Make the Course entity type an abstract type.

  5. Map the inherited CourseID properties of the OnlineCourse and OnsiteCourse entity types to the appropriate columns.

Prerequisites

To complete this walkthrough you must first build the CourseManager application. For more information and instructions, see the Entity Framework Quickstart. You will implement table-per-type inheritance by modifying conceptual model used in the CourseManager application. You will then extend the application's functionality to separately display online and onsite courses for a selected department.

Note

Because many of the walkthrough topics in this documentation use the CourseManager application as a starting point, we recommend that you use a copy of the CourseManager application for this walkthrough, instead of editing the original CourseManager code.

The walkthrough assumes that the reader has basic competency with Visual Studio, the .NET Framework, and programming using either Visual C# or Visual Basic.

Implementing Table-per-Type Inheritance

In this procedure, you will alter the SchoolModel by implementing table-per-type inheritance. To watch a video presentation of the following procedure, see How Do I: Model Table-per-Type Inheritance with the Entity Framework Tools.

To implement table-per-type inheritance

  1. Open the CourseManager solution in Visual Studio.

  2. In Solution Explorer, double-click the School.edmx file.

    The School.edmx file opens in the Entity Data Model Designer (Entity Designer).

  3. Right-click the OnlineCourse entity type and select Properties.

  4. In the Properties window, set the Base Type property to Course.

  5. Repeat steps 3 and 4 for the OnsiteCourse entity type.

  6. Right-click the association (the line) between the OnlineCourse and Course entity types. Select Delete.

  7. Right-click the association between the OnsiteCourse and Course entity types. Select Delete.

  8. Right-click the CourseID property of the OnlineCourse entity type, and then select Delete.

  9. Right-click the CourseID property of the OnsiteCourse entity type, and then select Delete.

  10. Select the Course entity type. Set its Abstract property to true in the Properties window.

    A message box appears to tell you that defining an entity type as abstract will remove all existing function mappings for that type. Click OK.

    Note

    Because the Course entity type has now been changed into an abstract type, the update functionality of the original CourseManager application will not work.

  11. Right-click the OnlineCourse entity type and select Table Mapping.

    The Mapping Details window appears.

  12. Click the Value/Property field that corresponds to the CourseID column.

    The Value/Property field becomes a drop-down list of properties that are available to map to the corresponding column.

  13. Select CourseID from the drop-down list.

  14. Repeat steps 11 through 13 for the OnsiteCourse entity type.

Table-per-type inheritance is now implemented.

Constructing the User Interface

Next, you will add a button to the CourseViewer form that loads and displays the CourseDiscrimForm form. Then, you will add two DataGridView controls for displaying OnsiteCourses and OnlineCourses. Finally, you will bind the DataGridView to BindingSource controls. For more information about binding objects to controls, see Binding Objects to Controls (Entity Framework).

To construct the user interface

  1. Right-click the CourseManager project in the Solution Explorer, point to Add, and then select New Item.

    The Add New Item dialog box appears.

  2. Select Windows Form and then click Add.

    A new form is added to the project and opens in the form designer.

  3. In the Properties window, set the name of the form to CourseDiscriminator.

    A new form is added to the project and opens in the form designer. The name of the form is set to CourseDiscriminator and the text is set to CourseDiscriminator.

  4. Drag a ComboBox control to the form from the Toolbox and set its name to departmentList in the Properties window.

  5. Drag a DataGridView control to the form from the Toolbox and set its name to onsiteGridView.

  6. Drag another DataGridView control from the Toolbox to the form and set its name to onlineGridView.

  7. In the Solution Explorer, double-click CourseViewer.cs or CourseViewer.vb.

    The design view of the CourseViewer form appears.

  8. Drag a Button control from the Toolbox to the CourseViewer form.

  9. In the Properties window, set the name of the Button to viewDiscriminator and set the text of the button to Online vs. Onsite.

  10. Double-click the viewDiscriminator Button.

    The code-behind file for the form opens.

  11. Add the following code to the viewDiscriminator_click event handler:

    Dim courseDiscrim As New CourseDiscriminator
    courseDiscrim.Visible = True
    
    CourseDiscriminator courseDiscrim = new CourseDiscriminator();
    courseDiscrim.Visible = true;
    

The user interface for this form is now complete.

Querying the Conceptual Model

In this procedure, you will query the conceptual model and bind the results to Windows Forms controls.

To query the conceptual model

  1. Open the CourseDiscriminator form in the form designer.

  2. Drag a BindingSource control from the Toolbox to the form.

  3. In the Properties window, set the name of the BindingSource to onsiteBindingSource.

  4. Click the field next to the DataSource property in the Properties window.

    The field becomes a drop-down list of available data sources.

  5. Click Add Project Data Source.

    The Choose a Data Source Type window of the Data Source Configuration Wizard opens.

  6. In the Where will the application get data from? box, select Object.

  7. Click Next.

    The Select the Object You Wish to Bind to window opens. The top node of a tree of available resources is displayed.

  8. Expand the CourseManager node, then expand the child CourseManager node.

  9. Select OnsiteCourse and click Finish.

  10. Repeat steps 2 through 9, but name the BindingSource control OnlineBindingSource and bind it to the OnlineCourse object.

  11. Right click the OnsiteGridView control and select Properties.

  12. In the Properties window, click the field next to the DataSource property.

    The field becomes a drop-down list of available data sources.

  13. Select OnsiteBindingSource

  14. Repeat steps 11 through 13 for the OnlineGridView, but set the DataSouce property to OnlineBindingSource.

    Note

    The OfType method returns an enumeration from which a DataGridView control cannot generate columns at run time. To display the desired information in this example, the data sources for the DataGridView controls are set at design time through BindingSource controls, based on the classes we want to bind to.

  15. Double-click the CourseDiscriminator form.

    The code-behind file for the CourseDiscriminator form opens.

  16. Add the following using (C#) or Imports (Visual Basic) statements to reference the model that you created from the School database and the entity namespace.

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    
  17. Add a property representing the data context to the CourseDiscriminator class:

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    
    // Create an ObjectContext instance based on SchoolEntity.
    private SchoolEntities schoolContext;
    
  18. In the CourseDiscriminator_Load event handler, add code to initialize the object context and bind the departmentList control to a query that returns Department information.

    ' Initialize the ObjectContext.
    schoolContext = New SchoolEntities()
    
    ' Define a query that returns all Department objects and 
    ' related Course objects, ordered by name.
    Dim departmentQuery As ObjectQuery(Of Department) = _
        schoolContext.Departments.Include("Courses").OrderBy("it.Name")
    
    ' Bind the ComboBox control to the query, which is
    ' executed during data binding.
    departmentList.DisplayMember = "Name"
    departmentList.DataSource = departmentQuery. _
        Execute(MergeOption.OverwriteChanges)
    
    // Initialize the ObjectContext.
    schoolContext = new SchoolEntities();
    
    // Define a query that returns all Department objects  
    // and related Course objects, ordered by name.
    ObjectQuery<Department> departmentQuery = 
        schoolContext.Departments.Include("Courses").
        OrderBy("it.Name");
    
    // Bind the ComboBox control to the query.
    this.departmentList.DisplayMember = "Name";
    this.departmentList.DataSource = departmentQuery.
        Execute(MergeOption.OverwriteChanges);
    
  19. Return to the designer view of the CourseDiscriminator form and double-click the departmentList ComboBox control.

    The code-behind file opens. A departmentList_SelectedIndexChanged event handler has been added to the code.

  20. Add the following code to the departmentList_SelectedIndexChanged event handler to update the data sources for the BindingSource controls.

    ' Get the selected department object.
    Dim department As Department = CType(Me.departmentList. _
            SelectedItem, Department)
    
    ' Update the data sources for the BindingSource controls.
    onsiteBindingSource.DataSource = department.Courses _
        .OfType(Of OnsiteCourse)()
    onlineBindingSource.DataSource = department.Courses _
        .OfType(Of OnlineCourse)()
    
    // Get the selected department object.
    Department department = (Department)this.departmentList
        .SelectedItem;
    
    // Update the data sources for the BindingSource controls.
    onsiteBindingSource.DataSource = department.Courses.
        OfType<OnsiteCourse>();
    onlineBindingSource.DataSource = department.Courses.
        OfType<OnlineCourse>();
    

The application is now complete. Press Ctrl+F5 to run the application. Click the Onsite vs. Online button to load the CourseDiscriminator form. OnsiteCourses and OnlineCourses for the selected department are displayed in the DataGridView controls.

Code Listing

This section lists the final version of the code-behind file for the CourseDiscriminator form.

Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class CourseDiscriminator

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities

    Private Sub CourseDiscriminator_Load(ByVal sender As System. _
        Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Initialize the ObjectContext.
        schoolContext = New SchoolEntities()

        ' Define a query that returns all Department objects and 
        ' related Course objects, ordered by name.
        Dim departmentQuery As ObjectQuery(Of Department) = _
            schoolContext.Departments.Include("Courses").OrderBy("it.Name")

        ' Bind the ComboBox control to the query, which is
        ' executed during data binding.
        departmentList.DisplayMember = "Name"
        departmentList.DataSource = departmentQuery. _
            Execute(MergeOption.OverwriteChanges)
    End Sub

    Private Sub departmentList_SelectedIndexChanged(ByVal sender As  _
        System.Object, ByVal e As System.EventArgs) Handles _
        departmentList.SelectedIndexChanged
        ' Get the selected department object.
        Dim department As Department = CType(Me.departmentList. _
                SelectedItem, Department)

        ' Update the data sources for the BindingSource controls.
        onsiteBindingSource.DataSource = department.Courses _
            .OfType(Of OnsiteCourse)()
        onlineBindingSource.DataSource = department.Courses _
            .OfType(Of OnlineCourse)()
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

namespace CourseManager
{
    public partial class CourseDiscriminator : Form
    {
        // Create an ObjectContext instance based on SchoolEntity.
        private SchoolEntities schoolContext;

        public CourseDiscriminator()
        {
            InitializeComponent();
        }

        private void CourseDiscriminator_Load(object sender,
            EventArgs e)
        {
            // Initialize the ObjectContext.
            schoolContext = new SchoolEntities();

            // Define a query that returns all Department objects  
            // and related Course objects, ordered by name.
            ObjectQuery<Department> departmentQuery = 
                schoolContext.Departments.Include("Courses").
                OrderBy("it.Name");

            // Bind the ComboBox control to the query.
            this.departmentList.DisplayMember = "Name";
            this.departmentList.DataSource = departmentQuery.
                Execute(MergeOption.OverwriteChanges);
        }

        private void departmentList_SelectedIndexChanged(object sender,
            EventArgs e)
        {
            // Get the selected department object.
            Department department = (Department)this.departmentList
                .SelectedItem;

            // Update the data sources for the BindingSource controls.
            onsiteBindingSource.DataSource = department.Courses.
                OfType<OnsiteCourse>();
            onlineBindingSource.DataSource = department.Courses.
                OfType<OnlineCourse>();
        }
    }
}

Next Steps

You have successfully implemented table-per-type inheritance in a conceptual model. For more information about how to define a conceptual model with table-per-type inheritance, see How to: Define a Model with Table-per-Type Inheritance (Entity Framework). For more information about how to build applications that use the Entity Framework, see ADO.NET Entity Framework.

See Also

Other Resources

Entity Data Model Tools Scenarios
Entity Data Model Tools Tasks