Chapter 17. Using the Entity Data Model

In Chapter 16, "Developing Data-Driven Applications," you learned how to use DataSets, how to perform data binding, and how to use LINQ to SQL. As it happens, LINQ to SQL is not the only object-relational technology Microsoft has to offer.

In this chapter, you'll discover Microsoft's latest data technology, the Entity Framework. Released initially with Service Pack 1 of Microsoft .NET Framework 3.5, it is the 2010 version of Visual Studio that ships with this data technology out of the box for the first time. While LINQ to SQL is a somewhat lightweight, code-oriented data technology, the Entity Framework is a comprehensive, model-driven data solution.

The Entity Framework represents a central piece of Microsoft's long-term data access strategy. With its emphasis on modeling and on the isolation of data and application layers, it promises to deliver a powerful data platform capable of supporting the applications through a complete application life cycle.

For a Visual Basic programmer, it brings working with the data under the familiar object-oriented mantle and provides numerous productivity enhancements. You will be able to construct "zero SQL code" data applications, leverage LINQ when working with data, and change the underlying data store without any impact on your application. In a few short words, using the Entity Framework to work with data is a whole different ballgame.

In this chapter, you'll learn how to do the following:

  • Employ deferred loading when querying the Entity Data Model

  • Use entity inheritance features in the Entity Framework

  • Create and query related entities

The Entity Framework: Raising the Data Abstraction Bar

In Chapter 15, "Programming with ADO.NET," you saw traditional .NET Framework techniques for working with data. In stream-based data access, you use a DataReader to read from the data store (typically a relational database) and can use the Command object to modify the data in the data store. Set-based data access encapsulates data operations through the DataSet object, whose collection of DataTable objects closely mimics the structure of tables or views in the database. A DataSet lets you work with data in disconnected mode, so you can load the data from the database into the application, disconnect, work on the data, and finally connect and submit modifications to the database in a single operation.

Both techniques provide a well-known way to work with data in your Visual Basic application. The DataSet goes one step further than stream-based data access in providing the programming abstraction for data access that hides many of the complexities of low-level data access. As a result, you will have to write a lot less SQL code. Neither method, however, provides a higher-level abstraction of the underlying database structure. This interdependence between your application and the data layer is problematic for several reasons, as you will see in the next section.

The Entity Framework brings another level of abstraction to the data layer. It lets you work with a conceptual representation of data, also known as a conceptual schema, instead of working with the data directly. This schema is then projected to your application layer, where code generation is used to create a .NET representation of your conceptual schema. Next, the Entity Framework generates a relational (or logical) schema used to describe the data model in relational terms. Finally, mapping between the relational schema and .NET classes is generated. Based on this data, the Entity Framework is capable of creating and populating .NET objects with the data from a data store and persisting modifications made on the object data back to the data store.

How Will You Benefit from the Entity Framework?

One famous programming aphorism states that "all problems in computing can be solved by another level of indirection." Although the Entity Framework introduces new level of indirection (and abstraction), you will see that this additional level is actually put to a good use. I'll show you the problems that the folks at Microsoft tried to tackle with the Entity Framework and how they managed to resolve them.

Preserving the Expressiveness of the Data Model

If you have a lot of experience working with relational databases, especially with databases that have been around for some time and have been through numerous modifications, you must have been puzzled by the actual meaning of some elements in a database. Questions like the following might ring a bell: What is this column used for? Why is this set of data duplicated between tables? Why is this set of columns in a table empty in certain rows?"

A good understanding of your customer's needs and business is crucial for the success of the application you will be developing. This understanding can be written down in the form of requirements and together with the description of the business (or problem domain) will be indispensable for the design of your application.

An important part of the design of many applications is the data structure that the system will use. One of the most popular methods for designing the data is the entity-relationship model (ERM). The ERM is a conceptual representation of data where the problem domain is described in the form of entities and their relationships. In Visual Studio, this model is called the Entity Data Model (EDM), and you will learn how to create the EDM in the next section. Figure 17.1 shows the sample Entity Data Model diagram inside Visual Studio 2010.

Entities generally can be identified by the primary key and have some important characteristics known as attributes. For example, a person entity might have a primary key in the form of their Social Security number (SSN) and attributes First and Last Name. (Although an SSN conceptually fits well in the role of a primary key and therefore I chose it for the primary key in the Person table in the example Books and Authors project later in this chapter, in practice its use is discouraged. See "Using a Social Security Number as a Primary Key" in the following sidebar for more information.)

Entity Data Model diagram in Visual Studio 2010's EDM Designer

Figure 17.1. Entity Data Model diagram in Visual Studio 2010's EDM Designer

An entity can be in a relationship with another entity. During the analysis, you can often hear this relationship expressed in the form of a simple sentence, such as "A person owns a pet." In such a sentence, nouns are entities, and verbs represent a relationship. In this case, a person is related to a pet. An important characteristic of a relationship is cardinality, or the numeric aspect of the relation between entities. In our example, a person can own many pets, but a pet usually belongs to a single person, thus being a one-to-many relationship between the person and pet entities.

The most popular method used to work with the ERM is an entity-relationship diagram. Many tools have smart entity-relationship diagramming capabilities, including the ERwin Data Modeler, Microsoft Visio, Toad Data Modeler, and Visual Studio. I will describe the Visual Studio entity-relationship capabilities in the "Creating a New Entity Data Model" section. These tools are typically capable of transforming the conceptual to a physical model — generating Data Definition Language (DDL) scripts that can be used to generate a physical database structure.

When working with relational databases on an implementation level, you create tables and columns, constraints, and primary and foreign keys to hold your data, and you create indices to optimize data access and manipulation. Although these database concepts can be related to a problem domain so that table roughly corresponds to entity, column corresponds to attribute, and foreign key constraint corresponds to relationship, they are not as expressive as the entity-relationship model. In addition, the physical design of a relational database is governed by a different set of principles and needs. Databases are very good at preserving data integrity, performing transactions, providing fast access to a data, and reducing data redundancy. As a result, the relational database's physical design is often refined through a process of normalization and denormalization. This process is typically in the domain of database administrators, who use their knowledge of database engines to optimize database performance, often with little regard for the problem domain at hand.

It is during this process that the link between the problem domain (described in the form of an Entity Data Model) and the physical database structure is watered down. Later in the application life cycle, the Entity Data Model is often completely disregarded. As a result, database structure becomes a cryptic artifact, difficult to relate to a problem domain. This often has an adverse effect on application maintainability and evolution. When the link between the two is weakened, small changes to the application can require a huge amount of implementation work just to understand the inner workings of the database.

With the Entity Framework, Microsoft has tackled this problem by making the Entity Data Model an integral part of your application. The Entity Data Model is to generate native .NET classes used to access the data store. These classes are mapped to tables in the database. The Entity Framework uses the Entity Data Model as a basis for .NET code and database structure generation; this sets it apart from typical modeling tools. The model becomes integral part of the project, driving the database and .NET code design.

A Richer Set of Modeling Constructs for Representing Data

To represent an entity in a relational database, you use a table construct. The table itself represents an entity type, while each row represents one specific instance of an entity. Columns are used to represent entity attributes. When you define an attribute, you choose a data type for it. To refine attribute definition, you can apply a constraint on an attribute, or you can make the attribute a primary key, meaning that it will uniquely identify the entity. You can relate different entities by defining foreign keys between two tables.

Following this approach (I am sure you are already very familiar with it), you can easily represent customers and product categories in a simple CRM system. The system will store basic customer data and information on a customer's favorite product categories. You can define a Customers table to represent the customers in your database. Important customer attributes are the first and last names, and in order to save this information in the Customer table, you can define FirstName and LastName columns whose data type is varchar with maximum length of 50. You can use a Social Security number or a database-generated integer as a primary key. For product categories, you can define a ProductCategories table with an Id column for the primary key and a Name column as varchar with a maximum length of 20.

In simple scenarios like the one I just described, at first glance objects in relational database will represent your entities fairly well. However, there are many situations where this approach will fall short. Let's examine a few such situations in our simple CRM system.

Complex Type for Complex Properties

You will want to keep each customer's telephone number in the database. Keep one telephone number per customer, but split that telephone number into country code, area code, and local number columns. This way, you can easily add checks on the validity of the data and perform some area code–related customer analysis. There are two ways to add telephone information: You can add three new columns to the existing Customers table, or you can create a new Telephones table for these three columns. The Telephones table can have a one-to-one relation with the Customers table.

In the first scenario, in order to keep the meaning of the columns clear, you will have to prefix the column names with Telephone, so you will have TelephoneCountryCode, TelephoneAreaCode, and TelephoneNumber columns. Although keeping long column names is not such a terrible burden, it is a good indicator that the attributes that these columns represent in fact belong to another entity — Telephone.

Representing Telephone as a separate entity is achieved by placing the columns in separate table called Telephones with the addition of a customer primary key column so that each telephone is tied to a single customer. Now there is no need to prefix column names with the word Telephone, since the purpose of the columns is clearly stated through a table name. Note that there is no difference on the database level in representing a one-to-one relation or a one-to-many relation. If you use a separate table for the Telephone entity, then the same structure used for storing a single telephone number per customer can be used for storing multiple telephone numbers for an individual customer.

Unfortunately, keeping two entities with a one-to-one relationship in separate tables in a database will probably result in processing overhead: The database engine needs to join the two tables and duplicate the Social Security number in order to join them. As such, in the eyes of the database administrator, the Telephones table is a good candidate for merging with the Customers table during the performance optimization process. If the merger happens, it is even possible that the original column names are kept. You end up with a mysterious Number column in the Customers table. Since the Number column no longer belongs to the Telephone table, the purpose of the column is not easily understood from its name.

In the Entity Framework, you can use a complex type construct as an attribute of an entity. In our example, you can declare a new Telephone complex type and add a Telephone attribute (of type Telephone) to the Customer entity. Thanks to this feature of the Entity Framework, you will be able to reference telephone number–related properties in your Visual Basic code in the form of Customer.Telephone.AreaCode.

Many-to-Many as a Simple Relation Between Entities

I am sure that it comes as no surprise that you will need an additional table, called a join table, to relate the customers and product categories. The CustomersProductCategories relation table will have only two columns: SSN and ProductCategoriesId. To complete the solution, two foreign keys are added. The first foreign key is established between the SSN column in Customer and the SSN column in CustomersProductCategories. The second one is between the Id column in ProductCategories and the ProductCategoriesId column in the CustomersProductCategories table. What I have just described is a typical approach used to represent a many-to-many relationship in a relational database. You can see the EDM entities and database tables representing many-to-many relation between customers and product categories in Figure 17.2.

Many-to-many relationship table structure (left) and Entity Data Model representation (right)

Figure 17.2. Many-to-many relationship table structure (left) and Entity Data Model representation (right)

As you can see, the additional table in this case is necessary to represent a relation between two entities. Sometimes this relation can grow into a full-blown entity, such as when it needs to be described with some attributes. For example, imagine you need to store an amount spent on each category for each customer. Such a need would result in adding an AmountSpent column to the CustomersProductCategories table and would warrant treating CustomersProductCategories as an entity. However, in the scenario I just described, a relation between customers and product categories is just a relation and should be represented as such. In relational databases, you are left with no choice but to use the table to represent a many-to-many relation, even though tables are generally used to represent full-blown entities.

In the Entity Framework, as long as you do not need to store any relation attributes, the relation will be treated as such. You will see how the many-to-many relation is created in the "Creating a New Entity Data Model" section later in this chapter.

Inheritance Applied to Data

As a Visual Basic programmer, you are quite familiar with the concept of inheritance. Inheritance combined with polymorphism is a powerful mechanism for harnessing reuse in software.

With the Entity Framework, a similar inheritance concept can be applied to entities in the Entity Data Model. Since entities are mapped to generated .NET classes, the inheritance relation between entities is harnessed in your application code.

Data Store Technology and Brand Independence

Standard ADO .NET classes are doing a good job of encapsulating access to different data stores. If you are careful enough and you follow the "Program to an interface, not an implementation" principle, you will significantly reduce the amount of the application code you need to modify in case you need to change the data store used by your application. The "Program to an abstraction" principle applied to ADO .NET means writing code using top-level interfaces from the System.Data namespace, like in the following example:

Dim connection As System.Data.IDbConnection = CreateConnection(connectionString)
Dim command As System.Data.IDbCommand = connection.CreateCommand()

As long you do not reference any class from any concrete ADO .NET provider namespace (like System.Data.SqlClient in the case of a Microsoft SQL Server provider), switching your application to another data store can be as simple as changing the connection string — that is, as long as you are able to write your SQL code in a dialect that all data stores are able to understand. If you write command text along the same lines as the previous example, like this:

command.CommandText = "Select top 10 * from Customers"

you might find that your database does not support the TOP keyword. Although there are different standards trying to regulate SQL, the truth is that there are many proprietary extensions to the language. Writing portable SQL is difficult and often impractical.

With the Entity Framework, you have a number of query options. You can use Entity SQL (eSQL), LINQ, or Query Builder methods. Whatever your choice, you are guaranteed that the query will return the same result no matter the data store under scrutiny. Thanks to the ADO.NET Entity Framework provider architecture, new data stores can be easily incorporated and made available to .NET programmers. What's more, there is no restriction on the underlying data store technology. Most will be relational databases, but as long as the appropriate provider is available, other technologies such as object-oriented databases, databases based on BigTable technology, Excel spreadsheets, and so on, will be available through the Entity Framework. Now you know why I insisted on using the term data store instead of database so far in this chapter.

Isolating the Application from the Data Structural Changes

During the application lifetime, the data and programmatic layers are generally exposed to different forces governing their evolution. The object-oriented layer accommodates evolution by preserving modularity and providing extensibility, while the data layer is influenced by forces such as referential integrity, normalization, and performance optimization.

As a database is exposed to more intensive use and the quantity of the stored data increases, the database structure often has to be re-accommodated to respond to an increase in demand. One such common scenario is table partitioning.

A table might be split so that rarely used columns containing less used but weighty pieces of information are placed in a separate table. This type of data partitioning strategy is known as vertical partitioning. By contrast, horizontal partitioning involves placing rows into different, identically structured tables. It is often used as a form of archiving; historic data that cannot be deleted but is rarely used is placed in a separate table.

The Entity Framework supports a number of mapping scenarios. It is capable of mapping a single entity to multiple tables and can use any or all of the following forms:

  • Horizontal or vertical partitioning

  • Complex types that structure the data contained in a single table

  • Entity type hierarchies

  • Mapping views

  • Stored procedures for database interaction

With all these mapping options at your disposal, many of the typical database modifications, especially those that are the result of performance tuning, can be accommodated at the mapping layer. This way, even though the database structure changes, no changes need be applied to your .NET code. The Entity Framework's mapping capability can isolate your code from structural changes in the database layer.

Entity Data Model: Model-First Approach

The fundamental concept in the Entity Framework is the Entity Data Model (EDM). The EDM is an implementation of the entity-relationship model, and it defines entities and their relationships. Entities and relationships define a conceptual model. In addition, the EDM contains a logical model, known as the storage schema model, that defines the data store structure. Finally, a section in the EDM defines the mapping between the conceptual and logical schemas.

In the first release of the Entity Framework (.NET 3.5 Service Pack 1), the only way to create an EDM was to connect to an existing database and let Visual Studio create entities based on the existing database structure. Although this approach can work for existing projects, for a new project that is based on reverse engineering, it would result in a loss of important information in the conceptual model.

In Visual Studio 2010, you can start with a blank EDM. You use the EDM Designer to create and modify the EDM. The EDM Designer is a visual modeling tool that displays the model in the form of a entity-relationship diagram.

Using the EDM Designer

The EDM Designer is displayed by default when you add a new ADO .NET EDM to your project or click an EDM file (.edmx extension) in Visual Studio. Figure 17.3 shows the EDM Designer with the Northwind EDM open.

The EDM Designer in Visual Studio 2010

Figure 17.3. The EDM Designer in Visual Studio 2010

The EDM diagram is displayed in the central window. You can access many options of the EDM Designer through a context menu that appears if you right-click anywhere on the empty surface of an EDM diagram.

You can add new items to the EDM diagram by dragging and dropping tools from the Toolbox window. The Toolbox window is the window shown on the left side in Figure 17.3. Once you select an item in the EDM diagram, you can change its properties in the Properties window, pictured on the right side in Figure 17.3 and positioned below the Model Browser window.

You can see elements of the EDM grouped by type in the Model Browser window. If an EDM is complex, then right-clicking a relationship or an entity in the Model Browser and selecting Show In Designer from the context menu can be a much more practical option for finding your way around the model.

Finally, at the bottom of the Figure 17.3 you can see a Mapping Details window. In this window, you can define how entities and relations from your conceptual model are mapped to tables in the logical model. Let's start by creating a new project with a fresh EDM.

Creating a New Entity Data Model

You can add an EDM to a majority of project types supported by Visual Studio 2010. For this exercise, you will start by creating a new Windows Forms project:

  1. Open a new instance of Visual Studio 2010, and choose File

    Creating a New Entity Data Model
  2. Choose Project

    Creating a New Entity Data Model
  3. When the Entity Data Model Wizard opens, choose Empty Model in response to the "What should the model contain?" prompt. Click Finish, and save the project.

You have just created a new EDM. After you created a new EDM, Visual Studio displays the EDM Designer with your BooksAndAuthors.edmx file open in the active window.

Connecting the EDM to a Database

You can create and model your entities in the EDM Designer on a conceptual level without ever using it to connect to a real database. This way, however, your model will be no more than a dead diagram. To breathe some life into your EDM, you need to connect it to a database. Start by creating a new BooksAndAuthors database in SQL Server 2005 or newer. Use the instructions that follow:

  1. In your SQL Server instance, create a BooksAndAuthors database.

  2. In your Visual Studio Server Explorer window, right-click the Data Connections item, and click the Add Connection item on the context menu.

  3. Add a new connection to the BooksAndAuthors database you just created.

  4. Right-click the BooksAndAuthors.edmx item in the Model Browser window, and select Model

    Connecting the EDM to a Database
  5. In the Generate Database Script Wizard window, select the BooksAndAuthors connection in the Connection combo box. Confirm that the Save Entity Connection Settings In App.Config File As check box is selected. Click Next.

  6. Click Finish.

  7. Click Yes on any warning windows that appear.

Check the Solution Explorer. You should see that a new BooksAndAuthors.edmx.sql file has been added to the MyEFProject. This SQL file contains a Data Definition Language (DDL) script that can be used to create a database structure that can accommodate the BooksAndAuthors EDM.

Note that the EDM Designer only creates the DDL file; it does not execute it against the database. Don't execute it just yet. Let's add some entities to our model first.

Creating an Entity

You can now add your first entity to BooksAndAuthors EDM. In this exercise, you will create an entity model for a publishing company. It will contain information on book titles and authors. Start by creating a new Book entity:

  1. Open the Toolbox, and drag an Entity item to the BooksAndAuthors.edmx designer surface. You will see a new square figure called Entity1 appear on the EDM Designer surface.

  2. Click the entity name, and rename it from Entity1 to Book.

  3. Rename the Entity Set Name property in the Properties window to Books.

Notice that an important characteristic of an entity is that it can be uniquely identified. An entity is generally identified by an attribute or a combination of attributes known as a primary key. The EDM Designer uses the term property for attributes.

Creating a Primary Key

In the case of the Book entity, the EDM Designer automatically created an Id property and marked it as a primary key. If you select the Id property in the EDM Designer, the Properties window will display characteristics of the Id property of the Book entity. The important characteristics of the Id property are Type and Entity Key. The Type = Int32 entry in the Properties windows indicates that the data type of the Id property is Integer. The Entity Key = True entry tells you that Id is a primary key.

Although you could use an artificial primary key, in the case of a Book entity, there is another property that is a better candidate for the primary key. All book titles can be uniquely identified by their ISBN numbers.

To use the ISBN for a primary key of Book entity, follow these steps.

  1. In the Properties window, change the name of the Id property to ISBN.

  2. Then, change the Type value of the ISBN property to String.

  3. Finally, since ISBN numbers have a maximum length of 13 characters, set the Max Size characteristic of the ISBN property to 13.

Creating a Scalar Property

The most important property of the Book entity is the title. The title is a simple string, so it can be well represented as a scalar property of the Book entity. Let's add a Title scalar property to the Book entity.

  1. On the EDM Designer surface, right-click the word Properties on the Book entity, and select the Add item from the context menu.

    The Add item expands to two subitems: Scalar Property and Complex Property.

  2. Click Scalar Property.

  3. Enter the word Title for the newly added property name.

  4. In the Properties window, set the Max Length value of the Title property to 4000. (According to WikiAnswers.com, the longest book title consists of 3,999 characters; it is too long to be reproduced here!)

While you are at it, use the same process to add another scalar property called PublishingDate to the Book entity. Select DateTime as the property type.

Yet another important property for a book is the page count. It is a good idea to preserve this information, so add another scalar property named PageCount to the Book entity, and select Int32 as the Type.

Entity Data Model Under the Hood

Most of the time, you will be interacting with the EDM through the EDM Designer. Nevertheless, you should have a basic understanding of the artifacts that comprise the EDM and its structure. The EDM native format is XML, and it can also be viewed and edited manually, as can any XML file. To see the Visual Studio–generated EDM XML, first refresh the model and then open the EDM file in the Visual Studio XML Editor:

  1. Refresh the EDM by regenerating the database DDL and by following the process described in steps 4 to 7 in the "Connecting the EDM to a Database" section earlier in this chapter.

  2. Close the EDM diagram.

  3. In Solution Explorer, right-click the BooksAndAuthors.edmx file, and select Open With from the context menu.

  4. In the Open With dialog box, select the XML Editor, and click OK.

Listing 17.1 shows the content of the BooksAndAuthors.edmx file. Although the content might look bewildering at first, it is actually not that complex; it is even easier to understand if you ignore the XML namespace declaration. You can see that the content is divided into four main sections:

  • SSDL content

  • CSDL content

  • C-S mapping content

  • EF Designer content

Example 17.1. The BooksAndAuthors.edmx model XML content

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="2.0"
 xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">
  <!-- EF Runtime content -->
  <edmx:Runtime>
    <!-- SSDL content -->
    <edmx:StorageModels>
    <Schema Namespace="BooksAndAuthors.Store" Alias="Self"
    Provider="System.Data.SqlClient"
    ProviderManifestToken="2008"
    xmlns:store="http://schemas.microsoft.com
    /ado/2007/12/edm/EntityStoreSchemaGenerator"
    xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="BooksAndAuthorsStoreContainer">
    <EntitySet Name="Books" EntityType="BooksAndAuthors.Store.Books"
     store:Type="Tables" Schema="dbo" />
  </EntityContainer>
  <EntityType Name="Books">
    <Key>
      <PropertyRef Name="ISBN" />
    </Key>
    <Property Name="ISBN" Type="varchar" Nullable="false" MaxLength="13" />
    <Property Name="Title" Type="nvarchar" Nullable="false" MaxLength="4000" />
    <Property Name="PublishingDate" Type="datetime" Nullable="false" />
    <Property Name="PageCount" Type="int" Nullable="false" />
  </EntityType>
</Schema></edmx:StorageModels>
    <!-- CSDL content -->
    <edmx:ConceptualModels>
      <Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
      xmlns:store="http://schemas.microsoft.com
      /ado/2007/12/edm/EntityStoreSchemaGenerator"
      Namespace="BooksAndAuthors" Alias="Self">
        <EntityContainer Name="BooksAndAuthorsContainer" >
          <EntitySet Name="Books"
                    EntityType="BooksAndAuthors.Book" />
        </EntityContainer>
        <EntityType Name="Book">
          <Key>
            <PropertyRef Name="ISBN" /></Key>
          <Property Type="String" Name="ISBN" Nullable="false"
                  MaxLength="13" Unicode="false" FixedLength="false" />
          <Property Type="String" Name="Title"
                    Nullable="false" MaxLength="4000" />
          <Property Type="DateTime" Name="PublishingDate"
                    Nullable="false" Precision="29" />
          <Property Type="Int32" Name="PageCount"
                    Nullable="false" /></EntityType></Schema>
    </edmx:ConceptualModels>
    <!-- C-S mapping content -->
    <edmx:Mappings>
    <Mapping Space="C-S" xmlns="http://schemas.microsoft.com
                               /ado/2008/09/mapping/cs">
  <EntityContainerMapping StorageEntityContainer="BooksAndAuthorsStoreContainer"
   CdmEntityContainer="BooksAndAuthorsContainer">
    <EntitySetMapping Name="Books">
      <EntityTypeMapping TypeName="IsTypeOf(BooksAndAuthors.Book)">
        <MappingFragment StoreEntitySet="Books">
          <ScalarProperty Name="ISBN" ColumnName="ISBN" />
          <ScalarProperty Name="Title" ColumnName="Title" />
          <ScalarProperty Name="PublishingDate" ColumnName="PublishingDate" />
<ScalarProperty Name="PageCount" ColumnName="PageCount" />
        </MappingFragment>
      </EntityTypeMapping>
    </EntitySetMapping>
  </EntityContainerMapping>
</Mapping></edmx:Mappings>
  </edmx:Runtime>
  <!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->
  <edmx:Designer xmlns="http://schemas.microsoft.com/ado/2008/10/edmx">
    <edmx:Connection>
      <DesignerInfoPropertySet>
        <DesignerProperty Name="MetadataArtifactProcessing"
         Value="EmbedInOutputAssembly" />
      </DesignerInfoPropertySet>
    </edmx:Connection>
    <edmx:Options>
      <DesignerInfoPropertySet>
        <DesignerProperty Name="ValidateOnBuild" Value="true" />
      </DesignerInfoPropertySet>
    </edmx:Options>
    <!-- Diagram content (shape and connector positions) -->
    <edmx:Diagrams>
      <Diagram Name="BooksAndAuthors" >
        <EntityTypeShape EntityType="BooksAndAuthors.Book"
                         Width="1.5" PointX="3.375" PointY="2"
                         Height="1.592306315104167" />
      </Diagram>
    </edmx:Diagrams>
  </edmx:Designer>
</edmx:Edmx>

So far, the file contains information on a single entity. As we continue working on the model, it will grow and become more complex. Fortunately, you can edit most of the model details through the EDM Designer and rarely need to edit the file manually.

The Conceptual Model: The CSDL Content

CSDL stands for Conceptual Schema Definition Language. This section contains information on the conceptual data model and corresponds directly to the content of the EDM diagram. This schema is the basis for the object model that is generated by Visual Studio as a .NET projection of a conceptual model.

The important elements of the CSDL schema are EntityType with Key and Property nodes. At this point you have a single Book entity in the model, and that entity has several properties, as you can see in Listing 17.1. The Key node references the ISBN property. Each Property node contains information, including property name, type, and nullability. This section also contains the information on the EntitySet, which is used to represent a set of entities. In this case, you'll find the Books EntitySet, as you defined it earlier in step 3 of the "Creating an Entity" section.

The Logical Model: The SSDL Content

This section is written in the Store Schema Definition Language (SSDL) and is a description of database structure that will be used to persist the data for the application build on the Entity Framework.

The structure of the SSDL section is quite similar to the CSDL section; it describes entities and associations. This section is used to generate DDL code and defines a store projection of the conceptual model. Entities and associations in the SSDL section define tables and columns in the storage model.

The Mapping Specification: C-S Mapping Content

The mapping specification is defined in the Mapping Specification Language (MSL). This is the place where the two worlds — .NET objects and the storage schema — meet. You can see how each entity maps to a table in the database and each property maps to a column.

Take a look the EntityTypeMapping tag inside the BooksAndAuthors.edmx file provided in Listing 17.1. Notice that the TypeName attribute has the value IsTypeOf(BooksAndAuthors.Book). IsTypeOf is just a way of saying that the type for this entity is Book (or any other class that inherits the Book type).

The MappingFragment inside the EntityTypeMapping defines the table to which the Book entity will be mapped via the StoreEntitySet attribute. In this case, the StoreEntitySet has the value Books, as you defined when you created the Book entity via the EntitySet property.

Finally, inside the MappingFragment you can see how different properties are mapped to columns in the table. For example, the ISBN property is mapped to a ISBN column: <ScalarProperty Name="ISBN" ColumnName="ISBN" />.

Data as Objects in the Entity Framework

The typical way to interact with the Entity Framework is through Object Services. Object Services is the component in the Entity Framework in charge of providing the .NET view of the data. For example, you will access the entity Book as a class Book in your .NET code. The code for the Book class is generated by the Entity Framework and is already available in the project. As with any other objects in .NET, you will be able to use LINQ to query these objects. Take a look at Listing 17.2; it shows how you can use LINQ to find a specific book based on ISBN.

Example 17.2. Using the Entity Framework–generated code to access the EDM

Dim context As New BooksAndAuthorsContainer
Dim books = context.Books
Dim myBook As Book = From book In books
 Where (book.ISBN = "455454857")
           Select book

To provide a native .NET view of the data in the EDM, the Entity Framework will generate Visual Basic code for partial classes that represent entities in your EDM. To take a look at this tool-generated code, follow these steps:

  1. Click the Show All Files icon in your Solution Explorer.

  2. Expand the BooksAnAuthors.edmx item in Solution Explorer.

  3. Click the BooksAndAuthors.Designer.vb file.

For brevity's sake, Listing 17.3 provides a portion of the code contained in the BooksAndAuthors.Designer.vb file. The listing will make much more sense if you keep in mind the way that classes are used; think about what you learned as you reviewed the code in Listing 17.2.

Example 17.3. Entity Framework–generated .NET code

Public Partial Class BooksAndAuthorsContainer
    Inherits ObjectContext
    '...
    Public ReadOnly Property Books() As ObjectSet(Of Book)
        Get
            If (_Books Is Nothing) Then
                _Books = MyBase.CreateObjectSet(Of Book)("Books")
            End If
            Return _Books
        End Get
    End Property

    Private _Books As ObjectSet(Of Book)
    '...
End Class

<EdmEntityTypeAttribute(NamespaceName:="BooksAndAuthors", Name:="Book")>
<Serializable()>
<DataContractAttribute(IsReference:=True)>
Public Partial Class Book
    Inherits EntityObject
    #Region "Factory Method"

    '" <summary>
    '" Create a new Book object.
    '" </summary>
    Public Shared Function CreateBook(iSBN As Global.System.String,
                                      title As Global.System.String,
                                      publishingDate As Global.System.DateTime,
                                      pageCount As Global.System.Int32) As Book
        Dim book as Book = New Book
        book.ISBN = iSBN

        book.Title = title

        book.PublishingDate = publishingDate
book.PageCount = pageCount

        Return book
    End Function

    <EdmScalarPropertyAttribute(EntityKeyProperty:=true, IsNullable:=false)>
    <DataMemberAttribute()>
    Public Property ISBN() As Global.System.String
        Get
            Return _ISBN
        End Get
        Set
            If (ISBN <> value) Then
                OnISBNChanging(value)
                ReportPropertyChanging("ISBN")
                _ISBN = StructuralObject.SetValidValue(value, False)
                ReportPropertyChanged("ISBN")
                OnISBNChanged()
            End If
        End Set
    End Property

    Private _ISBN as Global.System.String
    Private Partial Sub OnISBNChanging(value As Global.System.String)
    End Sub

    Private Partial Sub OnISBNChanged()
    End Sub

    <EdmScalarPropertyAttribute(EntityKeyProperty:=false, IsNullable:=false)>
    <DataMemberAttribute()>
    Public Property Title() As Global.System.String
        Get
            Return _Title
        End Get
        Set
            OnTitleChanging(value)
            ReportPropertyChanging("Title")
            _Title = StructuralObject.SetValidValue(value, False)
            ReportPropertyChanged("Title")
            OnTitleChanged()
        End Set
    End Property

    Private _Title as Global.System.String
    Private Partial Sub OnTitleChanging(value As Global.System.String)
    End Sub
Private Partial Sub OnTitleChanged()
    End Sub

    <EdmScalarPropertyAttribute(EntityKeyProperty:=false, IsNullable:=false)>
    <DataMemberAttribute()>
    Public Property PublishingDate() As Global.System.DateTime
        Get
            Return _PublishingDate
        End Get
        Set
            OnPublishingDateChanging(value)
            ReportPropertyChanging("PublishingDate")
            _PublishingDate = StructuralObject.SetValidValue(value)
            ReportPropertyChanged("PublishingDate")
            OnPublishingDateChanged()
        End Set
    End Property

    Private _PublishingDate as Global.System.DateTime
    Private Partial Sub OnPublishingDateChanging(value As Global.System.DateTime)
    End Sub

    Private Partial Sub OnPublishingDateChanged()
    End Sub

    <EdmScalarPropertyAttribute(EntityKeyProperty:=false, IsNullable:=false)>
    <DataMemberAttribute()>
    Public Property PageCount() As Global.System.Int32
        Get
            Return _PageCount
        End Get
        Set
            OnPageCountChanging(value)
            ReportPropertyChanging("PageCount")
            _PageCount = StructuralObject.SetValidValue(value)
            ReportPropertyChanged("PageCount")
            OnPageCountChanged()
        End Set
    End Property

    Private _PageCount as Global.System.Int32
    Private Partial Sub OnPageCountChanging(value As Global.System.Int32)
    End Sub

    Private Partial Sub OnPageCountChanged()
    End Sub

End Class

The BooksAndAuthorsContainer class is the entry point for accessing Object Services in our example. This class inherits the ObjectContext class from the System.Data.Objects namespace and has a property called Books. The Books property represents a set of Book objects.

The Book class represents a Book entity. The Book class has to inherit the EntityObject class from the System.Data.Objects namespace. The class has a number of properties, and each property of the class corresponds to a property of the Book entity in Books and Authors EDM. You will see how the classes are used in detail in the "Putting the EDM to Work" section later in this chapter.

Adding an Inheritance Relationship Between Entities

Now that you are finished creating the Book entity, as you might expect, the Author entity is the next to be created. It will be important to collect and store author data, such as name, contact information, and so forth, in this entity. Before you create the Author entity, however, a bit of analysis is in order.

As it happens, the publishing company in question also works with foreign language titles. In the case of a foreign language title, it is important to collect and store information about the translator: name, contact information, languages translated, and the like. Surely, this warrants another entity in our system — a Translator entity.

If you compare the Author and the Translator entities, you will see that they have a lot of common properties. If we were to add the Translator and Author entities to the EDM now, we would have to duplicate these properties on each. There must be some more efficient way to deal with replicated properties.

The solution is the same as it would be in a situation when you design classes where there "is a kind of" relationship between the entities in Visual Basic. A common parent entity can be extracted; that parent entity will contain the common properties. Let's call this entity Person. Once the Person entity exists, you can add Author and Translator entities and make them inherit the Person entity. Start by adding a new Person entity to the EDM:

  1. Add new entity to the EDM, and name it Person.

  2. Rename the Id property to SSN. Change Type to String, set Max Length to 9, and set the Fixed Length value to True.

  3. Add a FirstName scalar property (Type: String, Max Length: 50).

  4. Add a LastName scalar property (Type: String, Max Length: 50).

  5. Add a new entity to the EDM, and name it Author.

  6. Select the Inheritance arrow in the Toolbox, and connect Author and Person, going from Author to Person.

  7. Delete the Id property in the Author entity.

  8. Add another new entity to the EDM, and name it Translator.

  9. Select the Inheritance arrow in the Toolbox, and connect Translator and Person, going from Translator to Person.

  10. Delete the Id property in the Translator entity.

You have just created an inheritance hierarchy with one base entity, Person, and two child entities, Translator and Author. Let's add a few more details to our model.

Since you will never instantiate a Person entity in your code because you will always work with a more specific type, a Translator or an Author, you can mark the Person entity as abstract. In the Properties window, select the Person entity, and set the Abstract property to True.

There are a few more bits of information you need to store for each child type. For authors, the publishing company would like to store the date that the author first signed with the company. To accommodate this, add a new property called Signed (Type: DateTime) to the Author entity. For translators, you will also want to keep the information on languages that they translate from. Since this requires defining another related table, you can create this table after you see how many-to-many associations are defined in the "Adding a Many-to-Many Association" section later in this chapter.

The inheritance feature you have just used is one of the Entity Framework's most powerful capabilities. It solves the problem of mapping the inheritance relationship in an object-oriented sense to tables in a relational database and lets you leverage the inheritance and polymorphism capacities of .NET code without any friction with the data store.

Entity Framework Inheritance Mapping

Mapping a group of classes from an object-oriented paradigm to tables in a relational database can be accomplished intuitively, if the classes have some kind of an association relationship. For example, a class might have a property whose type is some other class, like a Car class that has a Wheels property whose type is a list of wheels. In such a case, each class maps to a table in a relational database, and the association between classes maps to a one-to-many relationship in the database that is enforced by a foreign key constraint.

The situation becomes much more complex if there is an inheritance relationship between classes. In our Books and Authors model, both the Author entity and the Translator entity inherit the Person entity. Such a relationship is not easily represented in a relational store.

The Entity Framework supports several strategies for mapping an inheritance hierarchy to a relational store. I will describe the two most commonly used:

Table-per-type inheritance

Under the table-per-type inheritance strategy, each type has its own table. The parent table (in our example, the Person table) contains all the common properties for all the entities in the hierarchy. The tables representing the child entities (Author and Translator in our example) contain only those properties that are unique to a particular entity. To be able to relate parent and child tables, the primary key column (named SSN in our example) must exist in all the tables in the hierarchy. Additionally, a foreign key constraint can be established between the parent and child tables. Foreign keys guarantee that a child table (Author or Translator in our example) can be inserted only if there is a related record in the parent (Person) table. To be able to retrieve a single Author from the data store, the query has to join the Person and Author columns using the primary key.

In other words, if the row with value 569125274 of the SSN column exists in both the Person and the Author tables, then the person is an author. If such a row exists in both the Person and the Translator tables, then the person is a translator. And since we marked the Person entity as Abstract, if the row with a value of 569125274 in the SSN column exists in the Person table, then there has to be exactly one row in either the Author or Translator table with the same value in the SSN column.

If the child tables (the Author and Translator tables in our example) contain a lot of columns, then this scenario represents a more efficient storage mechanism. Queries on a table-per-type structure, on the other hand, can be slower than on those in a table-per-hierarchy structure. In each query, at least two tables have to be joined. Let's compare this to a table-per-hierarchy mapping scenario.

Table-per-hierarchy inheritance

In the table-per-hierarchy inheritance mapping scenario, all entities in the hierarchy are mapped to a single table. A special discriminatory column is used to discern which entity each individual row represents. In the mapping section of an EntityTypeMapping element, this row is represented by a Condition element. For example, inside the EntityTypeMapping for an Author entity, this element could be written as <Condition ColumnName="PersonCategory" Value="0" />. This means that each row with a value of 0 in the PersonCategory column is an instance of an Author entity.

In our example, this means that both the Translator and Author entities are mapped to the Person table. In such scenario, the Person table has to contain both Author and Translator entity-specific columns. As a consequence and since each row in a table represents either an author or a translator, there will always be a number of empty cells in each row. In the case where a row represents an author, the translator-specific data will be empty, and vice versa. This is the weakness of this scenario; it is not the most efficient storage model. However, in cases where there are a small number of specific columns, the storage inefficiency is more than compensated for by query efficiency. Since there are no tables to be joined, queries tend to execute much faster.

At this point, you might be curious to see the scenario chosen to represent inheritance in our model by default and the exact database structure generated by the EDM Designer. You can regenerate the database script from the model using the process described in steps 4 through 7 in the "Connecting the EDM to a Database" section earlier in this chapter. Once that is complete, open the BooksAndAuthors.edmx.sql file. You should see a file that contains the section of code shown in Listing 17.4.

Example 17.4. DDL code for tables in the inheritance hierarchy

-- Creating table 'Persons'
CREATE TABLE [dbo].[Persons] (
    [SSN] char(9)  NOT NULL,
    [FirstName] nvarchar(50)  NOT NULL,
    [LastName] nvarchar(50)  NOT NULL
);
GO
-- Creating table 'Persons_Translator'
CREATE TABLE [dbo].[Persons_Translator] (
    [SSN] char(9)  NOT NULL
);
GO
-- Creating table 'Persons_Author'
CREATE TABLE [dbo].[Persons_Author] (
    [Signed] datetime  NOT NULL,
    [SSN] char(9)  NOT NULL
);
GO
-- --------------------------------------------------
-- Creating all Primary Key Constraints
-- --------------------------------------------------

-- Creating primary key on [SSN] in table 'Persons'
ALTER TABLE [dbo].[Persons] WITH NOCHECK
ADD CONSTRAINT [PK_Persons]
    PRIMARY KEY CLUSTERED ([SSN] ASC)
    ON [PRIMARY]
GO
-- Creating primary key on [SSN] in table 'Persons_Translator'
ALTER TABLE [dbo].[Persons_Translator] WITH NOCHECK
ADD CONSTRAINT [PK_Persons_Translator]
    PRIMARY KEY CLUSTERED ([SSN] ASC)
    ON [PRIMARY]
GO
-- Creating primary key on [SSN] in table 'Persons_Author'
ALTER TABLE [dbo].[Persons_Author] WITH NOCHECK
ADD CONSTRAINT [PK_Persons_Author]
    PRIMARY KEY CLUSTERED ([SSN] ASC)
    ON [PRIMARY]
GO

-- --------------------------------------------------
-- Creating all Foreign Key Constraints
-- --------------------------------------------------

-- Creating foreign key on [SSN] in table 'Persons_Translator'
ALTER TABLE [dbo].[Persons_Translator] WITH NOCHECK
ADD CONSTRAINT [FK_Translator_inherits_Person]
    FOREIGN KEY ([SSN])
    REFERENCES [dbo].[Persons]
        ([SSN])
    ON DELETE NO ACTION ON UPDATE NO ACTION
GO
-- Creating foreign key on [SSN] in table 'Persons_Author'
ALTER TABLE [dbo].[Persons_Author] WITH NOCHECK
ADD CONSTRAINT [FK_Author_inherits_Person]
    FOREIGN KEY ([SSN])
    REFERENCES [dbo].[Persons]
        ([SSN])
    ON DELETE NO ACTION ON UPDATE NO ACTION
GO

If you analyze the DDL code in Listing 17.4, you can see that separate tables for Author and Translator were created. In our model, the strategy chosen to map inheritance in the example is table-per-type. (In case you are wondering whether this model would be able to accommodate those poor souls that work both as authors and translators, the answer is "yes!" There is nothing preventing the same SSN from existing in both the Persons_Author and Persons_Translator tables.)

With this code, you have a hierarchy in place to represent authors and translators in the database. You aren't finished with these entities yet; there is still some personal data that the system needs to keep as part of a complete record of the authors and translators. Let's see how you can use a complex property to structure some entity property information.

Adding a Complex Property to an Entity

One important piece of contact information for authors and translators is a telephone number. The publishing house needs to maintain a record of one telephone number for each author and translator. This requirement can be easily solved by adding another property to the Person entity. By adding a Telephone property to the Person entity, both the Author and Translator entities will inherit it.

Instead of simply adding a string property to a Person entity, you can provide additional integrity and meaning to data if you structure the telephone number information. Typically, a telephone number consists of a country code, an area code, and a local number. Sometimes, you also need additional information, such as an extension number or daytime/evening qualifier.

In the EDM Designer, you can use a complex property to structure the information you do not want to model as a separate entity. You can add a scalar property to the Person entity by following these steps:

  1. Right-click the word Properties on the Person entity on the EDM Designer surface, and select the Add item in the context menu.

    The Add item is expanded into two subitems: Scalar Property and Complex Property.

  2. Click the Complex Property subitem.

    A new property named Complex Property is added to Person.

  3. Select and then rename the property to Phone.

  4. Click an empty area on the EDM Designer surface, and select the Add

    Adding a Complex Property to an Entity

    A new complex type named ComplexType1 is added to the model.

  5. Select ComplexType1 in the Model Browser window, and rename it to PhoneNumber in the Properties window.

  6. Right-click PhoneNumber in the Model Browser, and select Add

    Adding a Complex Property to an Entity
  7. Rename the property to CountryCode in the Properties window, and assign Max Length a value of 3 and Default Value a value of 1.

  8. Repeat steps 6 and 7, adding an Area Code property (type String with a maximum length of 3) and a Number property (type String with a maximum length of 15 — this should be enough for any out-of-ordinary international number). Finally, add an Extension for consistency property (type String with maximum length of 20).

  9. Go back to the Person entity in the EDM Designer, and select the Phone property.

  10. Click the combo Type in the Properties window. It should now contain a single value: PhoneNumber. Select PhoneNumber as the type for the Phone property of the Person entity.

Select the Person entity in the EDM Designer, and take a look at the Mapping Details window. You will see that properties of the Phone property are mapped to the same Persons table as rest of the Person scalar properties. The only difference is the column name. The Phone_ prefix is used to mark them as belonging to the Phone complex type. This will suffice to keep telephone numbers for authors and translators. Note that in the case where more than one telephone number for each Author/Translator needs to be kept in a database, you can model Telephone as a separate entity in a one-to-many relationship with Person, as described in the next section.

Another important part of contact information is an address. The publisher often needs to keep multiple addresses for authors and translators. The next section explains just how to do that.

Adding a One-to-Many Association

Start by adding a new entity called Address to the Books and Authors EDM. As with telephone numbers, you should bear in mind the international character of address information. Keep the Id primary key added by default, and set the StoreGeneratedPatter property to Identity. Now add the following scalar properties to the Address entity:

  • FirstLine (Type String, Max Length 50)

  • SecondLine (Type String, Max Length 50, Nullable True)

  • City (Type String, Max Length 50)

  • PostalCode (Type Sting, Max Length 20)

  • State (Type Sting, Max Length 20)

  • Country (Type Sting, Max Length 20)

Now perform the following steps:

  1. Select the Association tool in the Toolbox window.

  2. Connect the Person and Address entities; start dragging from the Person entity.

You will see the line connecting the Person and Address entities with 1, which is used to signify one side in a one-to-many association on the side of Person, and an asterisk, which is used to signify the many side in a one-to-many association on the side of Address.

Now, select the line in the EDM diagram, and take a look at Properties window. You will see that the name of the association is PersonAddress. Since the association is one-to-many, rename it to PersonAddresses so that the cardinality of association can be easily understood from its name. You have just created a one-to-many association in your model.

Entities in a one-to-many association expose a stronger type of relationship. They often have a dependant life cycle. For example, if you eliminate a person from the database, it makes no sense to keep the addresses that belong to that person in the database. The Entity Framework can take care of this issue for you. If you set the End1 OnDelete property to Cascade, the Entity Framework will delete all addresses belonging to the deleted person automatically. Make sure that the End1 OnDelete property of the PersonAddresses association is set to Cascade.

Adding a Many-to-Many Association

At this point, you have created the main entities in your model. You have modeled the Author, Translator, and Book entities. But, a crucial piece of the puzzle is still missing. You must have a means to connect authors and translators with their books. Typically, you expect to have a single author per book. However, it is not uncommon (especially for nonfiction titles) to have several authors per book. And it is to be expected that an author will deliver more than one title in his lifetime. There is a similar relationship between translators and titles.

You can say that the nature of the relation between books and authors and between books and translators is many-to-many. Since the relation with books is common to both authors and translators, the best way to represent it in our model without incurring repetition is to associate the Person entity with the Book entity using a many-to-many association.

  1. Select the Association tool in the Toolbox window.

  2. Connect the Person and Book entities; drag from the Person entity to the Book entity.

  3. Select the newly added association.

  4. In the Properties window, make sure that End 1 Multiplicity has the value of * (Collection of Person) and End 2 Multiplicity has the value of * (Collection of Book).

  5. Pluralize the names of the navigation properties. Rename the End1 navigation property to Books and the End2 navigation property to Persons.

The diagram in the EDM Designer should show the diamond and an asterisk on both sides of the line connecting the Person and Book entities. Also, both the Person entity and the Book entity should display some newly created navigation properties: Books for the Person entity and Persons for the Book entity. Navigation properties are a special kind of properties in a sense that they hold a reference to another entity or set of entities.

If you select the line connecting the Person and Book entities, you will see in the Mapping Details window that it maps to a separate table called PersonBook.

Now that you have seen how to establish a many-to-many association between entities, you can add a new entity called Language, with Id and Name scalar properties. Create a many-to-many association between the Language and Translator entities. This way, you will be able to store information on the languages that an individual translator works with, making translator information much more useful to the publisher.

Generate the Database Structure

So far, you only used the EDM Designer to generate the DDL code. You haven't actually executed the DDL against the database. By following these steps, you can easily use Visual Studio 2010 to execute the DDL script.

  1. Open BooksAndAuthors.edmx.sql in Visual Studio.

  2. Make sure that the T-SQL Editor toolbar is visible. If it is not, right-click the toolbar area, and make sure that T-SQL Editor is selected.

  3. In the T-SQL Editor toolbox, select the BooksAndAuthors database in the Database combo. (the BooksAndAuthors database was created in the "Connecting the EDM to a Database" earlier section.)

  4. Click the Execute SQL button in T-SQL Editor toolbox.

At this point, it would be interesting to compare the structure of the EDM with the structure of the database generated for the model. You can take a look at the database structure in Figure 17.4.

Books and Authors database structure

Figure 17.4. Books and Authors database structure

Figure 17.5 shows the EDM.

Books and Authors EDM

Figure 17.5. Books and Authors EDM

Although there is a correspondence between the diagrams, there are some significant differences. The EDM diagram shows the many-to-many association as a line between the two tables, while on the database diagram the many-to-many relation materializes in a joined table. Although the EDM diagram shows a single complex property, Phone, in a database, these are materialized as a group of columns clustered around the same Phone_ prefix.

In conclusion, the EDM is capable of providing a cleaner conceptual view of the data, can hide many low-level implementation details, and provides a much richer set of features for working with data.

With this, you have successfully generated your first Entity Data Model and used most of the available constructs while doing so. The model itself, though, is not much use if you are not able to interact with it and use it to store the data. In the next section, you will see how you can use the Entity Framework to obtain data from the data store and how you can modify that same data with the help of the Entity Framework.

Putting the EDM to Work

In a way, working with the Entity Framework is similar to working with DataSets. You query the EDM in order to obtain the objects representing the data. Next you display the objects; then you perform any updates, inserts, or deletes on objects; and finally you commit changes to the data store. The difference is that instead of working with objects such as a DataTable that represents the data store structure directly, you work with your entities that represent the objects in your business layer.

As I mentioned in the introductory section, the Entity Framework introduces another layer of abstraction when you are working with data. Although there are numerous benefits in productivity and simplification to be found in such an approach, an additional layer inevitably means letting go of some low-level control features. I am not obsessed by keeping things completely in control, but I do like to understand what is going on underneath the surface.

One very good tool that can help you understand how the Entity Framework interacts with the database is the SQL Server Profiler. It displays the exact SQL query that the Entity Framework issues to the database. This can help you understand a number of subtleties related to the Entity Framework and will allow you to optimize the way you use it. I suggest you use the SQL Server Profiler to monitor Entity Framework–to–database conversations until you become familiar with the Entity Framework's behavior.

In the next section, I will, from time to time, use the SQL Server Profiler trace output to corroborate some statements that I make about the inner workings of the Entity Framework.

Querying the Entity Data Model

At this point, you must be aching to write some Visual Basic code. In the following section, you will see some very interesting options that the Entity Framework provides for accessing the data. You will typically use LINQ to Entities to query the EDM. In this case, you see data in the form of strongly typed .NET objects exposed via the ObjectContext class and made available through the Object Services infrastructure. In cases where you need more granular control or need to write some dynamically generated queries, you can use the Entity SQL language. You can issue Entity SQL queries to query the ObjectContext class, or for more low-level access, you can use the EntityClient data provider. Finally, you can query the ObjectContext class by using Entity SQL with the help of the ObjectQuery class or by constructing your queries with the help of ObjectQuery Query Builder methods. Let's start with LINQ to Entities.

Using LINQ to Entities

In Chapter 14, "An Introduction to LINQ," you familiarized yourself with LINQ. Thanks to the Object Services layer and the .NET code generated by the Entity Framework (shown in Listing 17.3), you can query the data in the EDM as objects and unleash all the power of LINQ syntax in your Visual Basic code.

It is time you put to use the form added automatically to MyEFProject that contains the BooksAndAuthors EDM. Start by renaming the form to QueryEDM.vb, and add a button named ListAllBooks.

Listing All of the Entities in a Database

If you go back to Listing 17.3, you will see that the EDM Designer generated a class named BooksAndAuthorsContainer that inherits the ObjectContext class and that I introduced as the "entry point" to EDM Object Services. This class has a property, Books, of type ObjectSet(Of Book) that essentially represents a set of entities and implements interfaces like IQueryable and IEnumerable. With those in place, listing all book entities should be as easy as traversing all of the objects in the Books set of BooksAndAuthorsContainer instance. Let's do just that in a ListAllBooks button event handler. Take a look at Listing 17.5. It writes the title of all books in the database to the console. To see the result, make sure that the Output window in Visual Studio is visible.

Example 17.5. Listing all Book entities

Private Sub ListAllBooks_Click(ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles ListAllBooks.Click
    Dim context As New BooksAndAuthorsContainer
    For Each book In context.Books
        Console.WriteLine("Title: " & book.Title)
    Next
End Sub

This was actually very simple — a kind of Entity Framework Hello Word application. Let's now try something a bit more complicated. Let's see whether we can actually use some LINQ syntax.

Finding an Entity Using a LINQ Query

LINQ can be very expressive when writing queries. If you use it prudently, it can be also very efficient. Take a look at the code in Listing 17.6. It finds the longest book by ordering books by the PageCount property and then selecting the first Book entity on the list.

Example 17.6. Find the longest book LINQ query

Private Sub LongestBook_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles LongestBook.Click
Dim context As New BooksAndAuthorsContainer
    Dim books = context.Books
    Dim longestBook As Book = (From book In books
                    Order By book.PageCount).First
    Console.WriteLine("The longest book is: " +
    longestBook.ISBN + " " + longestBook.Title)
End Sub

If you take a look at Listing 17.7, you can see the SQL code issued to the database and captured by SQL Server Profiler.

Example 17.7. SQL Server Profiler–captured SQL code

SELECT TOP (1)
[Extent1].[ISBN] AS [ISBN],
[Extent1].[Title] AS [Title],
[Extent1].[PublishingDate] AS [PublishingDate],
[Extent1].[PageCount] AS [PageCount]
FROM [dbo].[Books] AS [Extent1]
ORDER BY [Extent1].[PageCount] ASC

Notice that the Entity Framework was intelligent enough to delegate the ordering operation to the database engine and to limit the result to a single row with the TOP statement. This is a much more efficient way to obtain the desired result than to write Visual Basic code that operates on the Books set. For example, you can obtain the same result by filtering the book by the PageCount property inside the For Each loop in Listing 17.5. This would result in fetching all the records in a table. Such code, however, would hardly be of production quality.

Finding an Entity of a Specific Type in the Inheritance Hierarchy

If you now open the BooksAndAuthors.Designer.vb file, you will see that it contains much more code than when we started building our Books and Authors EDM. If you inspect the BooksAndAuthorsContainer class, you might note something curious. The class has the Person property but no Translator or Author properties. So, how can you write the queries that reference a child type in an inheritance hierarchy?

The solution comes in the form of the LINQ OfType query operator. Take a look at Listing 17.8. It shows the code that fetches all translators stored in the Translators table.

Example 17.8. Fetch translators

Private Sub FetchTranslators_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles FetchTranslators.Click
    Dim context As New BooksAndAuthorsContainer
    Dim translators = context.Persons.OfType(Of Translator)()
For Each Translator In translators
        Console.WriteLine("Translator: " & Translator.FirstName &
                          " " & Translator.LastName)
    Next
End Sub

It is interesting to observe how this query resolves on the database level. Listing 17.9 shows the SQL issued to the database. Since the PersonTranslator hierarchy is modeled through a table-per-type inheritance modeling method, a join between the Persons and Translators tables has to be performed in order to fetch Translator entity rows from the database.

Example 17.9. Fetch translators SQL code

SELECT
'1X0X' AS [C1],
[Extent1].[SSN] AS [SSN],
[Extent2].[FirstName] AS [FirstName],
[Extent2].[LastName] AS [LastName],
1 AS [C2],
[Extent2].[Phone_CountryCode] AS [Phone_CountryCode],
[Extent2].[Phone_AreaCode] AS [Phone_AreaCode],
[Extent2].[Phone_Number] AS [Phone_Number],
[Extent2].[Phone_Extension] AS [Phone_Extension]
FROM  [dbo].[Persons_Translator] AS [Extent1]
INNER JOIN [dbo].[Persons] AS [Extent2] ON [Extent1].[SSN] = [Extent2].[SSN]

As you can see in the last line in the listing, each time you need to obtain an instance of a Translator or Author entity, a join between Person and Translator or Person and Author has to be performed on the database level.

Using Entity SQL

If you have spent some time writing the SQL code, you will immediately feel familiar with eSQL. You can think of it as a data store–agnostic SQL used to query the EDM. Take a look at the code in Listing 17.10. It shows a simple query issued to the Books and Authors EDM using the Entity Client classes. The code has the familiar, traditional, ADO.NET "open connection, execute command" feel to it.

Example 17.10. eSQL query issued using the Entity Client library

Private Sub FindBookByISBN_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles FindBookByISBN.Click
    Using connection As New EntityConnection("Name=BooksAndAuthorsContainer")
        connection.Open()
        Dim command = connection.CreateCommand()
command.CommandText = "SELECT VALUE book FROM" &
                               "BooksAndAuthorsContainer.Books " &
                               "As book Where book.ISBN = @isbn"
        command.Parameters.AddWithValue("isbn", "9780470187425")
        Dim reader = command.ExecuteReader(CommandBehavior.SequentialAccess)
        reader.Read()
        Console.WriteLine("Book title is: " & reader("Title"))
    End Using
End Sub

If you take a look at the SQL Profiler trace, you will see how eSQL was transformed to the parameterized SQL query. Listing 17.11 shows the trace.

Example 17.11. eSQL command transformed to parameterized query SQL

exec sp_executesql N'SELECT
[Extent1].[ISBN] AS [ISBN],
[Extent1].[Title] AS [Title],
[Extent1].[PublishingDate] AS [PublishingDate],
[Extent1].[PageCount] AS [PageCount]
FROM [dbo].[Books] AS [Extent1]
WHERE [Extent1].[ISBN] = @isbn',N'@isbn nvarchar(4000)',@isbn=N'9780470187425'

eSQL is a programming language in its own right, and, as such, getting into more details about eSQL programming is outside the scope of this book. For more information on eSQL, try the Microsoft Developer Network at http://msdn.microsoft.com/en-us/library/bb399560.aspx.

Using Query Builder Methods and the ObjectQuery Class

Yet another alternative you have at your disposal for querying the EDM is the ObjectQuery class. You can build your queries in a standard object-oriented manner using the ObjectQuery builder methods. The ObjectQuery API permits writing chained methods following the Builder pattern. It is best to illustrate this with an example. Listing 17.12 demonstrates query construction employing the Object Query API.

Example 17.12. ObjectQuery's builder methods

Private Sub QueryBuilder_Click(ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles QueryBuilder.Click
    Dim context As New BooksAndAuthorsContainer
    Dim query As ObjectQuery(Of Book)
    query = context.Books.
        Where("it.PublishingDate > DATETIME'1999-01-01 00:00'").
        OrderBy("it.PublishingDate").
        Top(10)
    Console.WriteLine(query.ToTraceString())
    For Each book As Book In query.ToList()
        Console.WriteLine(book.Title)
    Next
End Sub

ObjectQuery has a ToTraceString method that permits visualizing the SQL that is being issued to the database directly inside the Visual Studio. Using ToTraceString is a more simple SQL debug method than monitoring the trace in SQL Server Profiler. Listing 17.13 shows the SQL trace.

Example 17.13. ObjectQuery's ToTraceString method output

SELECT TOP (10)
[Extent1].[ISBN] AS [ISBN],
[Extent1].[Title] AS [Title],
[Extent1].[PublishingDate] AS [PublishingDate],
[Extent1].[PageCount] AS [PageCount]
FROM [dbo].[Books] AS [Extent1]
WHERE [Extent1].[PublishingDate] > convert(datetime2,
'1999-01-01 00:00:00.0000000', 121)
ORDER BY [Extent1].[PublishingDate] ASC

Deferred Loading and Navigation Properties

So far, I haven't demonstrated how you can use navigation properties to obtain a reference of a related entity. Now that you cannot use navigation properties (not using navigation properties would pretty much defeat the purpose of having the EDM), you just need to be explicit about the relations you will be using on entities obtained from your queries. There are several ways to tell the Entity Framework to fetch related entities as well as the resulting entity. For example, you can use the Include method of the ObjectQuery class, you can explicitly load related entities, or you can activate the deferred loading feature of the Entity Framework.

A very common scenario with the Books and Authors EDM would be to list all books authored by a certain author. Had I not spoiled the surprise in the previous paragraph, you might have expected the code in Listing 17.14 to list all books authored by the randomly selected first author in the database.

Example 17.14. Unsuccessful attempt of listing all books belonging to an author

Private Sub DefferedLoading_Click(ByVal sender As System.Object,
                                  ByVal e As System.EventArgs) Handles
                                  DefferedLoading.Click
    Dim context As New BooksAndAuthorsContainer
    Dim author = context.Persons.OfType(Of Author).First()
    For Each book As Book In author.Books
        Console.WriteLine(book.Title)
    Next
End Sub

If you take a look at the Output window after executing the code in Listing 17.14, you will notice it lists no books, even though all authors have at least one book that they have authored in the database.

Using the Include Method

The first alternative at your disposal that you can use to read or to materialize (as retrieving entities from the database is more commonly called in EDM jargon) the related entities is the Include method. This loading strategy is also known as eager loading. Take a look at Listing 17.15; it demonstrates the Include method used to materialize related Book entities.

Example 17.15. Using the Include, method to materialize related entities

Private Sub DefferedLoading_Click(ByVal sender As System.Object,
                                  ByVal e As System.EventArgs) Handles
                                  DefferedLoading.Click
Dim context As New BooksAndAuthorsContainer
    Dim author = context.Persons.OfType(Of Author).Include("Books").First()
    For Each book As Book In author.Books
        Console.WriteLine(book.Title)
    Next
End Sub

If you take a look at the Output window, you will see titles of the books belonging to the author printed to the console. More interesting, however, is the SQL generated by the Entity Framework. Listing 17.16 shows the SQL code.

Example 17.16. SQL code resulting from the Include method

SELECT
[Limit1].[Signed] AS [Signed],
[Limit1].[SSN] AS [SSN],
[Limit1].[SSN1] AS [SSN1],
[Limit1].[FirstName] AS [FirstName],
[Limit1].[LastName] AS [LastName],
[Limit1].[Phone_CountryCode] AS [Phone_CountryCode],
[Limit1].[Phone_AreaCode] AS [Phone_AreaCode],
[Limit1].[Phone_Number] AS [Phone_Number],
[Limit1].[Phone_Extension] AS [Phone_Extension],
[Limit1].[C1] AS [C1],
[Limit1].[C2] AS [C2],
[Limit1].[C3] AS [C3],
[Project2].[C1] AS [C4],
[Project2].[ISBN] AS [ISBN],
[Project2].[Title] AS [Title],
[Project2].[PublishingDate] AS [PublishingDate],
[Project2].[PageCount] AS [PageCount]
FROM   (SELECT TOP (1)
    [Extent1].[Signed] AS [Signed],
    [Extent1].[SSN] AS [SSN],
    [Extent2].[SSN] AS [SSN1],
    [Extent2].[FirstName] AS [FirstName],
    [Extent2].[LastName] AS [LastName],
    [Extent2].[Phone_CountryCode] AS [Phone_CountryCode],
    [Extent2].[Phone_AreaCode] AS [Phone_AreaCode],
    [Extent2].[Phone_Number] AS [Phone_Number],
    [Extent2].[Phone_Extension] AS [Phone_Extension],
    '1X0X' AS [C1],
    1 AS [C2],
    1 AS [C3]
    FROM  [dbo].[Persons_Author] AS [Extent1]
    INNER JOIN [dbo].[Persons] AS [Extent2] ON
        [Extent1].[SSN] = [Extent2].[SSN] ) AS [Limit1]
LEFT OUTER JOIN  (SELECT
[Extent3].[Persons_SSN] AS [Persons_SSN],
    [Extent4].[ISBN] AS [ISBN],
    [Extent4].[Title] AS [Title],
    [Extent4].[PublishingDate] AS [PublishingDate],
    [Extent4].[PageCount] AS [PageCount],
    1 AS [C1]
    FROM  [dbo].[PersonBook] AS [Extent3]
    INNER JOIN [dbo].[Books] AS [Extent4] ON
        [Extent4].[ISBN] = [Extent3].[Books_ISBN] )
        AS [Project2] ON [Limit1].[SSN] = [Project2].[Persons_SSN]
ORDER BY [Limit1].[SSN] ASC, [Limit1].[SSN1] ASC, [Project2].[C1] ASC

Listing 17.16 is quite long, but for our purpose, it is important to note that the code includes a join with the Books table. It is also the only query visible in the SQL Profiler trace, meaning that all the data was fetched in one go — meaning it was loaded eagerly. Loading can also be deferred, as you will see in the "Using Deferred Loading" section very soon.

Using Explicit Loading

Instead of including the whole path in your query, you can tell the navigation property to load just when you need it. Listing 17.17 shows how books related to an author can be materialized using the Load method of the EntityCollection class.

Example 17.17. Using explicit loading to materialize related entities

Private Sub DefferedLoading_Click(ByVal sender As System.Object,
                                  ByVal e As System.EventArgs) Handles
                                  DefferedLoading.Click
    Dim context As New BooksAndAuthorsContainer
    Dim author = context.Persons.OfType(Of Author).First()
    author.Books.Load()
    For Each book As Book In author.Books
        Console.WriteLine(book.Title)
    Next
End Sub

Again, it is the SQL Server Profiler that tells the true story of the code resolution on the database level. The trace includes two queries and is shown in Listing 17.18.

Example 17.18. SQL code resulting from the Load method

-- first trace
SELECT
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[SSN] AS [SSN],
[Limit1].[FirstName] AS [FirstName],
[Limit1].[LastName] AS [LastName],
[Limit1].[C2] AS [C2],
[Limit1].[Phone_CountryCode] AS [Phone_CountryCode],
[Limit1].[Phone_AreaCode] AS [Phone_AreaCode],
[Limit1].[Phone_Number] AS [Phone_Number],
[Limit1].[Phone_Extension] AS [Phone_Extension],
[Limit1].[Signed] AS [Signed]
FROM (SELECT TOP (1)
    [Extent1].[Signed] AS [Signed],
    [Extent1].[SSN] AS [SSN],
    [Extent2].[FirstName] AS [FirstName],
    [Extent2].[LastName] AS [LastName],
    [Extent2].[Phone_CountryCode] AS [Phone_CountryCode],
    [Extent2].[Phone_AreaCode] AS [Phone_AreaCode],
    [Extent2].[Phone_Number] AS [Phone_Number],
    [Extent2].[Phone_Extension] AS [Phone_Extension],
    '1X0X' AS [C1],
    1 AS [C2]
    FROM  [dbo].[Persons_Author] AS [Extent1]
    INNER JOIN [dbo].[Persons] AS [Extent2] ON [Extent1].[SSN] = [Extent2].[SSN]
)  AS [Limit1]

-- second trace
exec sp_executesql N'SELECT
[Extent2].[ISBN] AS [ISBN],
[Extent2].[Title] AS [Title],
[Extent2].[PublishingDate] AS [PublishingDate],
[Extent2].[PageCount] AS [PageCount]
FROM  [dbo].[PersonBook] AS [Extent1]
INNER JOIN [dbo].[Books] AS [Extent2] ON
[Extent1].[Books_ISBN] = [Extent2].[ISBN]
WHERE [Extent1].[Persons_SSN] =
@EntityKeyValue1',N'@EntityKeyValue1 char(9)',@EntityKeyValue1='568845586'

In this case, each time a book entity is referenced in the For Each loop, a new SQL statement is issued to a database. Had our author authored more than one book, you would end up with a significant number of queries issued to the database just to print this simple information.

Using Deferred Loading

More commonly known as lazy loading, this is the pattern that loads data from the database only upon the request. To put it simply, only when you call the Getter method of the navigation property is the data fetched from the database.

In this case, using navigation properties is quite transparent, and the code looks quite like the code in Listing 17.15. One missing detail has to do with activating the deferred loading, since it is deactivated by default. Listing 17.19 shows the code for deferred loading.

Example 17.19. Using deferred loading to materialize related entities

Private Sub DefferedLoading_Click(ByVal sender As System.Object,
                                  ByVal e As System.EventArgs) Handles
                                  DefferedLoading.Click
    Dim context As New BooksAndAuthorsContainer
    context.ContextOptions.DeferredLoadingEnabled = True
    Dim author = context.Persons.OfType(Of Author).First()
    For Each book As Book In author.Books
        Console.WriteLine(book.Title)
    Next
End Sub

As you would expect, the resulting SQL is identical to that shown in Listing 17.18, meaning that a new query is issued for each referenced Book entity.

Choosing the Right Loading Strategy

Now that you have seen how different loading strategies are resolved at a database level, you must be already guessing the impact of choosing the right loading strategy on the performance of your application. In our sample code, deferring the loading of book information would result in abysmal performance if such code was ever put into production.

If you had no prior experience with ORM frameworks like the Entity Framework, then at this point you might be asking yourself, "Does deferred loading make sense at all?" It does provide some coding benefits, since you use navigation properties just like any other properties, but this coding gain is certainly not worth the possible negative impact on the application performance.

Deferred (or lazy loading) is the default loading strategy in many object-relational mapping (ORM) frameworks similar to the Entity Framework. As it happens, the sample code I have shown in this section serves to illustrate different loading strategies, but it is not very representative of how real-life applications work.

In a typical application scenario, you use simple properties on an entity to display the entity information to the user. In our example, you use Author information (probably a name) to let the user select the correct author. (At this point, eagerly loading book information from the database would mean loading almost all the database data in one go!) Only after the user has selected the author is the related data (in our example list of books) fetched from the database. At this point, it does make sense to fetch all the books belonging to an author in one go, since you will show the list of all titles on the Author Details page. Imagine the Book has some related Sales information. It does not make sense to fetch this information until the user selects the specific title. With lazy loading, all the data is timely fetched.

The more complex the application becomes, the more complicated it is to understand all the scenarios where navigation properties will be used. For best results, fine-tune your queries, choose the correct loading strategy for each individual query, and profile the Entity Framework SQL generation to grasp how the Entity Framework queries are translated into the SQL issued to database.

Modifying the Data with the Entity Framework

The Entity Framework provides a strongly typed, object-oriented view of the data, and as such, it enables you to insert, update, and delete data by working with instances of entity types. All of the entity type instances are bound to a context (through an ObjectContext class) that tracks the changes made to these objects. Once you tell ObjectContext to commit changes to the database by calling the SaveChanges method, it calculates the differences between the data in the application and the data in the data store and will commit the changes to the data store.

In the Books and Authors EDM, the class inheriting the ObjectContext class is called BooksAndAuthorsContainer. As in the previous examples, when querying the data, BooksAndAuthorsContainer is the central class that permits access to all the objects' methods related to data modifications. Let's start with a simple insert of a new entity.

Inserting New Instances

We can insert a new instance of an entity in several ways. Each entity class in the EDM has a static CreateEntityName method. For example, the Book entity has a CreateBook static method that can be used for the purpose of adding a new book to a context. Alternatively, you can create a new entity using the New operator and then use the AddObject method that's available on each EntitySet class. Listing 17.20 shows the latter method where first a new instance of the Language entity is created, then it is added to the Languages entity set, and finally it is committed to the database by calling ObjectContext.SaveChanges().

Example 17.20. Adding a new Language entity

Private Sub InsertNewLanguage_Click(ByVal sender As System.Object,
                                ByVal e As System.EventArgs) Handles
                                InsertNewLanguage.Click
    Dim context As New BooksAndAuthorsContainer
    Dim arabic As New Language
    arabic.Name = "Arabic"
    context.Languages.AddObject(arabic)
    context.SaveChanges()
End Sub

Deleting an Entity Instance

Deletion of an entity instance can be easily performed with the help of the Delete method of ObjectContext. Listing 17.21 shows how this is performed using the instance of the Language entity inserted in the previous listing.

Example 17.21. Deleting a Language entity

Private Sub DeleteLanguage_Click(ByVal sender As System.Object,
                                 ByVal e As System.EventArgs) Handles
                                 DeleteLanguage.Click
    Dim context As New BooksAndAuthorsContainer
    Dim arabic As Language = (From language In context.Languages
                             Where language.Name = "Arabic").
                             FirstOrDefault()
    context.DeleteObject(arabic)
    context.SaveChanges()
End Sub

Updating an Entity Instance

You will not be surprised by the code included in Listing 17.22. It follows a pattern that is similar to that found in the entity insertion and deletion code. An Entity property is updated, and changes are committed by calling the ObjectContext SaveChanges method.

Example 17.22. Updating a Language entity

Private Sub UpdateLanguage_Click(ByVal sender As System.Object,
                                 ByVal e As System.EventArgs) Handles
                                 UpdateLanguage.Click
    Dim context As New BooksAndAuthorsContainer
    Dim arabic As Language = (From language In context.Languages
                             Where language.Name = "Arabic").
                             FirstOrDefault()
    arabic.Name = "Arabic Language"
    context.SaveChanges()
End Sub

Establishing Relationship Between Entity Instances

The final piece of the data modification puzzle has to do with establishing the relationship between the entity instances. If I have a Book instance and an Author instance, how do I relate them so that the book appears in the list of books authored by that specific author?

You can perform this in a standard object-oriented manner. Just add the Book instance to the set of books belonging to the Author, and call SaveChanges on the ObjectContext. Listing 17.23 shows that exact scenario.

Example 17.23. Establishing a relationship between an Author and a Book instance

Private Sub InsertsInTransaction_Click(ByVal sender As System.Object,
                                       ByVal e As System.EventArgs) Handles
                                       InsertsInTransaction.Click
    Dim context As New BooksAndAuthorsContainer

    Dim book As New Book With {.ISBN = "9780470179796",
                .Title = "Professional Refactoring in Visual Basic",
                .PageCount = "517", .PublishingDate = "April 7, 2008"}

    Dim author As New Author With {.SSN = "423235332",
                .FirstName = "Danijel",
                .LastName = "Arsenovski",
                .Phone = New PhoneNumber With {.CountryCode = "56",
                    .AreaCode = "2", .Number = "8588656", .Extension = " "},
                .Signed = "January 1, 2007"}

    context.Persons.AddObject(author)
    context.Books.AddObject(book)
author.Books.Add(book)
    context.SaveChanges()
End Sub

By calling the Add method on the set of Books belonging to an Authorauthor.Books.Add(book) — the Entity Framework becomes aware that two instances are related and adds the record to the PersonBook join table in the database.

Using Transactions in the Entity Framework

If you look back at Listing 17.23, you might wonder what exactly happens if the Entity Framework is not capable of performing all the operations in the listing. If you analyze the listing, you can see that the code will be resolved as three Insert operations on the database level: the insert of an author instance to the Author table, the insert of a book instance into the Book table, and the insert of the record relating the book and the author into the PersonBook table. So, what happens if one of these operations fails?

The Entity Framework generates implicit transactions for all changes performed from one SaveChanges method call to the next. In our example, all inserts will be committed to the database under the same transaction. If any of them fails, the others are automatically rolled back.

You can test this by executing the code in Listing 17.23 twice in a row. For the second execution, modify the author's Social Security number, but leave the book data as it is. Since you have modified the SSN, you will be able to insert the author, but the book insert will fail. ISBN is the primary key on the Book entity, and the same value cannot be inserted twice. The execution will end up in an error. If you then take a look at the database, you will observe that neither the book nor the author instance was inserted.

Coordinating Transactions with External Operations

In some scenarios, the transaction control that ObjectContext implicitly provides is not enough. There are situations where you need to enroll operations external to the ObjectContext into the same transaction. In such cases, you can use the TransactionScope class. Listing 17.24 shows some mock-up code that demonstrates how the TransactionScope class can be used.

Example 17.24. Using TransactionScope for enlisting external operations

Using transaction As New TransactionScope()
    context.Persons.AddObject(author)
    context.Books.AddObject(book)
    author.Books.Add(book)
    context.SaveChanges()
    'Do some external operation that can be enroled inside the transaction
    transaction.Complete()
End Using

Note that you need to reference the System.Transactions assembly and import the System.Transactions namespace in order to gain access to the TransactionScope class.

Reverse-Engineering an Entity Data Model

The approach I followed thus far in this chapter starts with the premise that you are building a completely new EDM. The EDM is then used to generate the DDL scripts for the database used to store the EDM data.

Very often, though, you will have to construct your application on the top of an existing database. The Entity Framework supports that scenario, as well; in that case, the EDM Designer will use the database structure as a starting point for automated EDM generation.

Let's explore this scenario by creating an EDM from the Northwind database. I explained how you can obtain and install the sample Northwind database in Chapter 15, so I will assume that the database is present in your Microsoft SQL Server.

  1. Choose Project

    Reverse-Engineering an Entity Data Model
  2. When the Entity Data Model Wizard opens, choose Generate From Database as your reply to the "What should the model contain?" prompt. Click Next.

  3. Select a connection to the Northwind database. If the connection is not present in the Connection combo box, use the New Connection button to create a connection to the Northwind database, and then click Next.

  4. When the Choose Your Database Objects window opens, select all the objects displayed under the Tables, Views, and Stored Procedures nodes, and click Finish.

The part of the Northwind EDM diagram showing entities generated from tables in the Northwind database was shown earlier in Figure 17.1.

The Entity Framework is capable of making use of already constructed databases including other database objects, such as views and stored procedures. In the case of views, a separate EntityType and EntitySet are generated for each view. Stored procedures are represented as functions in the EDM and can be mapped as custom CRUD operations for an entity. In that case, instead of generating the appropriate SQL, the Entity Framework is capable of invoking the stored procedure to delete, update, insert, or read the entity.

The Bottom Line

Employ deferred loading when querying the Entity Data Model.

The Entity Framework supports the deferred loading (lazy loading) of entities. When deferred loading is activated, entities in navigation properties are loaded on demand, only after they are accessed. In cases when you are not certain that the related entity will be accessed (for example, when loading is dependent on a user action), you can initially load only the root entity and load the related entities only when they are requested.

Master It

How do you activate deferred loading in the Entity Framework?

Use entity inheritance features in the Entity Framework.

In the EDM, an inheritance relationship can be established between entities. When two entities participate in an inheritance relationship, the child entity inherits all the properties of the parent entity. When working with such an entity in .NET code, you can get major benefits from code reuse based on inheritance.

Master It

Explain how the Entity Framework can map an inheritance relationship between entities to tables in the database. Why is maintaining the inheritance relationship between the entities not easily accomplished with relational databases?

Create and query related entities.

In the Entity Data Model, you can establish one-to-many or many-to-many associations between entities. The association can be established by connecting related entities with the Association tool in the EDM diagram. When querying such entities, a related entity or set of entities can be accessed through generated navigation properties.

Master It

In the Books and Authors application (used as a sample application in this chapter), add a SalesByMonth form that will display the number of copies sold in a month for each title in the database. Modify the Books and Authors EDM so that the model can accommodate monthly sales information for each title.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.27.178