Chapter 29. The ADO.NET Entity Framework

WHAT'S IN THIS CHAPTER?

  • Understanding the Entity Framework

  • Creating an Entity Framework model

  • Querying Entity Framework models

One of the core requirements in business applications (and many other types of applications) is the ability to store and retrieve data in a database. However, that's easier said than done, because the relational schema of a database does not blend well with the object hierarchies that we prefer to work with in code. To create and populate these object hierarchies required a lot of code to be written to transfer data from a data reader into a developer-friendly object model, which was then usually difficult to maintain. In fact, it was such a source of constant frustration that many developers turned to writing code generators or various other tools that automatically created the code to access a database based on its structure. However, code generators usually created a 1:1 mapping between the database structure and the object model, which was hardly ideal either, leading to a problem called "object relational impedance mismatch," where how data was stored in the database did not necessarily have a direct relationship with how developers wanted to model the data as objects. This led to the concept of Object Relational Mapping, where an ideal object model could be designed for working with data in code, which could then be mapped to the schema of a database. Once the mapping is complete, the Object Relational Mapper (ORM) framework should take over the burden of translating between the object model and the database, leaving developers to focus on actually solving the business problem (rather than focusing on the technological issues of working with data).

To many developers, ORMs are the Holy Grail for working with data in a database as objects, and there's no shortage of debate over the strengths and pitfalls of the various ORM tools available, and how an ideal ORM should be designed. We won't buy into these arguments in this chapter, but simply look at how to use the ADO.NET Entity Framework — Microsoft's ORM tool and framework.

Looking through history, the .NET Framework added a number of means to access data in a database since its inception, all under the banner of ADO.NET. First, we had low-level access through SqlConnection (and connections for other types of databases) using means like data readers. Then we had a higher-level means of accessing data using Typed DataSets. LINQ to SQL appeared in the .NET Framework 3.5, providing the first built-in way to work with data as objects.

However, for a long time Microsoft did not include an ORM tool in the .NET Framework (despite a number of earlier attempts to do so with the failed ObjectSpaces). There were already a number of ORMs available for use with the .NET Framework, with nHibernate and LLBLGen Pro being among the most popular. Microsoft did eventually manage to release its own, which it called the ADO.NET Entity Framework, and shipped it with the .NET Framework 3.5 SP1.

The Entity Framework's eventual release (despite being long awaited) was not smooth sailing either — with controversy generated before it was even released by a vote of no confidence petition signed by many developers, including a number of Microsoft MVPs. Indeed, it was the technology that provided the catalyst leading to the rise of the ALT.NET movement. However, since then there have been many improvements in the .NET Framework 4's Entity Framework implementation in order to reduce these perceived shortcomings.

This chapter takes you through the process of creating an Entity Framework model of a database, and how to query and update the database via it. The Entity Framework is a huge topic, with entire books devoted to its use. Therefore, it would be impossible to go through all its features, so this chapter focuses on discussing some of its core features and how to get started and create a basic entity model.

The Entity Framework model you create in this chapter will go on to be used in a number of subsequent chapters in this book where database access is required in the samples.

WHAT IS THE ENTITY FRAMEWORK?

Essentially, the Entity Framework is an Object Relational Mapper. Object Relational Mapping enables you to create a conceptual object model, map it to the database, and the ORM framework will take care of translating your queries over the object model to queries in the database, returning the data as the objects that you've defined in your model.

Comparison with LINQ to SQL

A common question from developers is regarding the Entity Framework's relationship with LINQ to SQL, and which technology they should use when creating data-centric applications. Let's take a look at the advantages each have over the other.

LINQ to SQL advantages over the Entity Framework:

  • Easy to get started and query

Entity Framework advantages over LINQ to SQL:

  • Enables you to build a conceptual model of the database rather than purely working with a 1:1 domain model of the database as objects (such as having one object mapped to multiple database tables, inheritance support, and defining complex properties).

  • Able to generate a database from your entity model.

  • Support for databases other than just SQL Server.

  • Support for many-to-many relationships.

  • Lazy loading and eager loading support.

  • Synchronization to get database updates will not lose your customizations to your model.

  • Will continue to evolve, whereas LINQ to SQL development will from now on be minimal.

Entity Framework Concepts

Here are some of the important concepts involved in the Entity Framework and some of the terms that are used throughout this chapter:

  • Entity Model: The entity model you create using the Entity Framework consists of three parts:

    • Conceptual model: Represents the object model, including the entities, their properties, and the associations between them.

    • Store model: Represents the database structure, including the tables/views/stored procedures, columns, foreign keys, and so on.

    • Mapping: Provides the glue between the store model and the conceptual model (that is, between the database and the object model), by mapping one to the other.

    Each of these parts is maintained by the Entity Framework as XML using a domain-specific language (DSL).

  • Entity: Entities are essentially just objects (with properties) to which a database model is mapped.

  • Entity Set: An entity set is a collection of a given entity. You can think of it as an entity being a row in a database, and an entity set being the table.

  • Association: Associations define relationships between entities in your entity model, and are conceptually the same as relationships in a database. Associations are used to traverse the data in your entity model between entities.

  • Mapping: Mapping is the core concept of ORM. It's essentially the translation layer from a relational schema in a database to objects in code.

GETTING STARTED

To demonstrate some of the various features in the Entity Framework, the example in this section uses the AdventureWorksLT sample database developed by Microsoft as one of the sample databases for SQL Server. AdventureWorksLT is a simpler version of the full AdventureWorks database, making it somewhat easier to demonstrate the concepts of the Entity Framework without the additional complexity that using the full database would create.

The AdventureWorksLT database is available for download from the CodePlex web site as a database script here:

http://professionalvisualstudio.com/link/1029A

Adventure Works Cycles is a fictional bicycle sales chain, and the AdventureWorksLT database is used to store and access its product sales data.

Follow the instructions from the CodePlex web site detailing how to install the database from the downloaded script in a SQL Server instance (SQL Server Express Edition is sufficient) that is on or can be accessed by your development machine.

Now move on to creating a project that contains an Entity Framework model of this database. Start by opening the New Project dialog and creating a new project. The sample project you create in this chapter uses the WPF project template. You'll be displaying data in a WPF DataGrid control defined in the MainWindow.xaml file named dgEntityFrameworkData.

Now that you have a project that will host and query an Entity Framework model, it's time to create that model.

CREATING AN ENTITY MODEL

You have two ways of going about creating an entity model. The usual means to do so is to create the model based on the structure of an existing database; however, with the Entity Framework it is also possible to start with a blank model and have the Entity Framework generate a database structure from it.

The sample project uses the first method to create an entity model based on the AdventureWorksLT database's structure.

The Entity Data Model Wizard

Open the Add New Item dialog for your project, navigate to the Data category, and select ADO.NET Entity Data Model as the item template (as shown in Figure 29-1). Call it AdventureWorksLTModel.edmx.

Figure 29-1

Figure 29-1. Figure 29-1

This will start the Entity Data Model Wizard that will help you get started building an Entity Framework model.

Figure 29-2

Figure 29-2. Figure 29-2

This will show the dialog shown in Figure 29-2 that enables you to select whether you want to automatically create a model from a database (Generate from Database), or start with an empty model (Empty Model).

The Empty Model option is useful when you want to take the approach of creating your model from scratch, and either mapping it manually to a given database, or letting the Entity Framework create a database based on your model.

However, as previously stated you will be creating an entity model from the AdventureWorksLT database, so for the purpose of this example use the Generate from Database option, and get the wizard to help you create the entity model from the database.

Figure 29-3

Figure 29-3. Figure 29-3

Moving onto the next step, you now need to create a connection to the database (as shown in Figure 29-3). You can find the most recent database connection you've created in the drop-down list, but if it's not there (such as if this is the first time you've created a connection to this database) you will need to create a new connection. To do so, click the New Connection button and go through the standard procedure of selecting the SQL Server instance, authentication credentials, and finally, selecting the database.

If you are using a username and password as your authentication details, you can choose not to include those in the connection string (containing the details required to connect to the database) when it is saved, because this string is saved in plain text that would enable anyone who sees it to have access to the database. In this case you would have to provide these credentials to the model before querying it in order for it to create a connection to the database. If you don't select the checkbox to save the connection settings in the App.config file you will also need to pass the model the details on how to connect to the database before you can query it.

Figure 29-4

Figure 29-4. Figure 29-4

In the next step, the wizard uses the connection created in the previous step to connect to the database and retrieve its structure (that is, its tables, views, and stored procedures), which is displayed in a tree for you to select the elements to be included in your model (see Figure 29-4).

Other options that can be specified on this screen include:

  • Pluralize or Singularize Generated Object Names: This option (when selected) intelligently takes the name of the table/view/stored procedure and pluralizes or singularizes the name based on how that name is used in the model (collections will use the plural form, entities will use the singular form, and so on).

  • Include Foreign Key Columns in the Model: The previous version of the Entity Framework did not create properties for foreign key columns in the entities — opting to create a relationship instead. However, in numerous scenarios this was not ideal, making it messy to do some simple tasks due to the absence of these properties. In this new version you can now select to include them in your entities by selecting this option.

  • Model Namespace: This enables you to specify the namespace in which all the classes related to the model will be created. By default, the model will exist in its own namespace (which defaults to the name of the model entered in the Add New Item dialog) rather than the default namespace of the project to avoid conflict with existing classes with the same names in the project.

Select all the tables in the database to be included in the model. Clicking the Finish button in this screen creates an Entity Framework model that maps to the database. From here you can view the model in the Entity Framework and adjust it as per your requirements and tidy it up as per your tastes (or standards) to make it ideal for querying in your code.

The Entity Framework Designer

Once the Entity Framework model has been generated, it opens in the Entity Framework designer, as shown in Figure 29-5.

Figure 29-5

Figure 29-5. Figure 29-5

You'll note that the designer has automatically laid out the entities that were created by the wizard, showing the associations it has created between them.

You can move entities around on the designer surface, and the designer will automatically move the association lines and try and keep them neatly laid out. Entities will automatically snap to a grid, which you can view by right-clicking the designer surface and selecting Grid

Figure 29-5

As you move entities around (or add additional entities to) the diagram, you may find it gets a little messy, with association lines going in all directions to avoid getting "tangled." To get the designer to automatically lay out the entities neatly again according to its own algorithms, you can right-click the designer surface and select Diagram

Figure 29-5

Entity Framework models can quickly become large and difficult to navigate in the Entity Framework designer. Luckily, the designer has a few tools to make navigating it a little easier. The designer enables you to zoom in and out using the zoom buttons in its bottom-right corner (below the vertical scrollbar — see Figure 29-6). The button sandwiched between these zoom in/out buttons zooms to 100% when clicked.

Figure 29-6

Figure 29-6. Figure 29-6

To zoom to a predefined percentage, right-click the designer surface and select one of the options in the Zoom menu. In this menu you will also find a Zoom to Fit option (to fit the entire entity model within the visible portion of the designer), and a Custom option that pops up a dialog enabling you to type a specific zoom level.

In addition, selecting an entity in the Properties tool window (from the drop-down object selector) automatically selects that entity in the designer and brings it into view; right-clicking the entity in the Model Browser tool window (described shortly) and selecting the Show in Designer menu item does the same. These make it easy to navigate to a particular entity in the designer, so you can make any modifications as required.

You can minimize the space taken by entities by clicking the icon in the top-right corner of the entity. Alternatively, you can roll up the Properties/Navigation Properties groupings by clicking the +/– icons to their left. Figure 29-7 shows an entity in its normal expanded state, with the Properties/Navigation Properties groupings rolled up and completely rolled up.

Figure 29-7

Figure 29-7. Figure 29-7

You can expand all the collapsed entities in one go by right-clicking the designer surface and selecting Diagram

Figure 29-7

A visual representation of an entity model (as provided by the Entity Framework designer) can serve a useful purpose in the design documentation for your application. The designer provides a means to save the model layout to an image file to help in this respect. Right-click anywhere on the designer surface and select Diagram

Figure 29-7

It can often be useful (especially when saving a diagram for documentation) to display the property types against each property for an entity in the designer. You can turn this on by right-clicking the designer surface and selecting Scalar Property Format

Figure 29-7

As with most designers in Visual Studio, the Toolbox and Properties tool windows are integral parts of working with the designer. The Toolbox (as shown in Figure 29-8) contains three controls: Entity, Association, and Inheritance. How to use these controls with the designer is covered shortly. The Properties tool window displays the properties of the selected item(s) in the designer (an entity, association, or inheritance), enabling you to modify their values as required.

Figure 29-8

Figure 29-8. Figure 29-8

In addition to the Toolbox and Properties tool windows, the Entity Framework designer also incorporates two other tool windows specific to it — the Model Browser tool window and the Mapping Details tool window — for working with the data.

The Model Browser tool window (as shown in Figure 29-9) enables you to browse the hierarchy of both the conceptual entity model of the database and its storage model. Clicking an element in the Store model hierarchy shows its properties in the Properties tool window; however, these can't be modified (because this is an entity modeling tool, not a database modeling tool). The only changes you can make to the Store model is to delete tables, views, and stored procedures (which won't modify the underlying database). Clicking elements in the Conceptual model hierarchy also shows their properties in the Properties tool window (which can be modified), and its mappings are displayed in the Mapping Details tool window. Right-clicking an entity in the hierarchy and selecting the Show in Designer menu item from the context menu brings the selected entity/association into view in the designer.

Figure 29-9

Figure 29-9. Figure 29-9

The second picture in Figure 29-9 demonstrates the searching functionality available in the Model Browser tool window. As previously discussed, because your entity model can get quite large, it can be difficult to find exactly what you are after. Therefore, a good search function is very important. Type your search term in the search textbox at the top of the window and press Enter. In this example the search term was Address, which highlighted all the names in the hierarchy (including entities, associations, properties, and so on) that contained the search term. You'll note that the vertical scrollbar has the places in the hierarchy (which has been expanded) highlighted where the search terms have been found, making it easy to see where the results were found throughout the hierarchy. The number of results is shown just below the search textbox, next to which are an up arrow and a down arrow to enable you to navigate through the results. When you are finished searching you can click the cross icon next to these to return the window to normal.

The Mapping Details tool window (as shown in Figure 29-10) enables you to modify the mapping between the conceptual model and the storage model for an entity. Selecting an entity in the designer, the Model Browser tool window, or the Properties tool window shows the mappings in this tool window between the properties of the entity to columns in the database. You have two ways of mapping the properties of an entity to the database: either via tables and views, or via functions (that is, stored procedures). On the left side of the tool window are two icons, enabling you to swap the view between mapping to tables and views, to mapping to functions. However, we'll focus here just on the features of mapping entity properties to tables and views.

Figure 29-10

Figure 29-10. Figure 29-10

The table/view mapping has a hierarchy (under the Column column) showing the table(s) mapped to the entity, with its columns underneath it. To these columns you can map properties on your entity (under the Value/Property column) by clicking in the cell, opening the drop-down list that appears, and selecting a property from the list.

A single entity may map to more than one database table/view (bringing two or more tables/views into a single entity, as previously discussed). To add another table/view to the hierarchy to map to your entity, click in the bottom row where it says <Add a Table or View> and select a table/view from the drop-down list. When you add a table to the Mapping Details tool window for mapping to an entity, it automatically matches columns with the same name to properties on the entities and creates a mapping between them. Delete a table from the hierarchy by selecting its row and pressing the Delete key.

Conditions are a powerful feature of the Entity Framework that enable you to selectively choose which table you want to map an entity to at run time based on one or more conditions that you specify. For example, say you have a single entity in your model called Product that maps to a table called Products in the database. However, you have additional extended properties on your entity that map to one of two tables based on the value of the ProductType property on the entity — if the product is of a particular type it will map the columns to one table, if it's another type it will map the columns to the other table. You can do this by adding a condition to the table mapping. In the Mapping Details window click in the row directly below a table to selectively map where it says <Add a Condition>. Open the drop-down list that appears, which will contain all the properties on the entity. Select the property to base your condition on (in the given example it would be the ProductType property), select an operator, and enter a value to compare the property to. Note that there are only two operators: equals (=) and Is. You can add additional conditions as necessary to determine whether the table should be used as the source of the data for the given properties.

Note

Note that a number of advanced features are available in the Entity Framework, but not available in the Entity Framework designer (such as working with the store schema, annotations, referencing other models, and so on). However, these actions can be performed by modifying the schema files (which are XML files) directly.

Creating/Modifying Entities

The Entity Data Model Wizard gave you a good starting point by building an entity model for you. In some cases this may be good enough and you can start writing the code to query it, but you can now take the opportunity to go through the created model and modify its design as per your requirements.

Because the Entity Framework is providing you with a conceptual model to design and work with, you are no longer limited to having a 1:1 relationship between the database schema and an object model in code, so the changes you make in the entity model won't affect the database in any way. So you may wish to delete properties from entities, change their names, and so on, and it will have no effect on the database. In addition, because any changes you make are in the conceptual model, updating the model from the database will not affect the conceptual model (only the storage model), so your changes won't be lost.

Changing Property Names

Often you will find yourself working with databases that have tables and columns containing prefixes or suffixes, over/under use of capitalization, or even names that no longer match their actual function. This is where the use of an ORM like the Entity Framework can demonstrate its power, because you can change all of these in the conceptual layer of the entity model to make the model nice to work with in code (with more meaningful and standardized names for the entities and associations), without needing to modify the underlying database schema. Luckily, the tables and columns in the AdventureWorksLT database have reasonably friendly names, but if you wanted to do so it would simply be a case of double-clicking the property in the designer (or selecting it and pressing F2), which changes the name display to a textbox enabling you to make the change. Alternatively, you can select the property in the designer, the Model Browser tool window, or the Properties tool window, and update the Name property in the Properties tool window.

Adding Properties to an Entity

Let's now look at the process of adding properties to an entity. Three types of properties exist:

  • Scalar properties: Properties with a primitive type, such as string, integer, boolean, and so on.

  • Complex properties: A grouping of scalar properties in a manner similar to a structure in code. Grouping properties together in this manner can make your entity model a lot more readable and manageable.

  • Navigation properties: Used to navigate across associations. For example, the SalesOrderHeader entity contains a navigation property called SalesOrderDetails that enables you to navigate to a collection of the SalesOrderDetail entities related to the current SalesOrderHeader entity. Creating an association between two entities automatically creates the required navigation properties.

The easiest way to try this for yourself is to delete a property from an existing entity and add it back again manually. Delete a property from an entity (select it in the designer and press the Delete key). Now to add it back again, right-click the entity and select Add

Adding Properties to an Entity

The next step is to set the type of the property, for which you'll have to move over to the Properties tool window to set. The default type is string, but you can change this to the required type by setting its Type property.

Properties that you want to designate as entity keys (that is, properties that are used to uniquely identify the entity) need their Entity Key property set to True. The property in the designer will have a picture of a little key added to its icon, making it easy to identify which properties are used to uniquely identify the entity.

You can set numerous other properties on a property, including assigning a default value, a maximum length (for strings), and whether it's nullable. You can also assign the scope of the getter and setter for the property (public, private, and so on), useful for, say, a property that will be mapped to a column with a calculated value in the database where you don't want the consuming application to attempt to set the value (by making the setter private).

The final task is to map the property to the store model. You do this as described earlier in the chapter using the Mapping Details tool window.

Creating Complex Types

Create a complex type on the Customer entity grouping the various customer name-related properties together in a complex type and thus making the Customer entity neater. Though you can create a complex type from scratch, the easiest way to create a complex type is to refactor an entity by selecting the scalar properties on the entity to be included in the complex type and having the designer create the complex type from those properties. Follow these instructions to move the name-related properties on the Customer entity to a complex type:

  • Select the name-related properties on the Customer entity (FirstName, LastName, MiddleName, NameStyle, Suffix, Title) by selecting the first property, and while holding down the Ctrl key selecting the other properties (so they are all selected at the same time).

  • Right-click one of the selected properties and select the Refactor into New Complex Type menu item.

  • In the Model Browser will be the new complex type that it created, with its name displayed in a textbox for you to name to something more meaningful. For this example, simply call it CustomerName.

  • The Entity Framework designer will have created a complex type, added the selected properties to it, removed the selected properties from the entity, and added the complex type that it just created as a new property on the entity in their place. However, this property will just have ComplexProperty as its name, so you will want to rename it to something more meaningful. Select the property in the designer, press F2, and enter Name in the textbox.

You will now find that by grouping the properties together in this way, the entity will be easier to work with in both the designer and in code.

Figure 29-11

Figure 29-11. Figure 29-11

Creating an Entity

So far you've been modifying existing entities as they were created by the Entity Data Model Wizard. However, let's now take a look at the process of creating an entity from scratch and then mapping it to a table/view/stored procedure in your storage model. Most of these aspects have already been covered, but we'll walk through the required steps to get an entity configured from scratch.

You have two ways of manually creating entities. The first is to right-click the designer surface and select Add

Creating an Entity

The other way of creating an entity is to drag and drop the Entity component from the Toolbox onto the designer surface. However, you'll note that it doesn't bring up the dialog from the previous method, instead opting to immediately create an entity with a default name, entity set name, and entity key property. You will then have to use the designer to modify its configuration to suit your needs.

The steps needed to finish configuring the entity are as follows:

  • If required, create an inheritance relationship by specifying that the entity should inherit from a base entity.

  • Create the required properties on the entity, setting at least one as an entity key.

  • Map these properties to the storage schema (using the Mapping Details tool window).

  • Create any associations with other entities in the model.

  • Validate your model to ensure that the entity is mapped correctly.

Note

All entities must have an entity key that can be used to uniquely identify the entity. Entity keys are conceptually the same as a primary key in a database.

As discussed earlier, you aren't limited to mapping to a single database table/view per entity. This is one of the benefits of building a conceptual model of the database — you may have related data spread across a number of database tables, but through having a conceptual entity model layer in the Entity Framework you are able to bring those different sources together into a single entity to make working with the data a lot easier in code.

Note

Make sure you don't focus too much on the structure of the database when you are creating your entity model — the advantage of designing a conceptual model is that it enables you to design the model based on how you plan to use it in code. Therefore, focus on designing your entity model, and then you can look at how it will map to the database.

Creating/Modifying Entity Associations

You have two ways of creating an association between two entities. The first is to right-click the header of one of the entities and select Add

Creating/Modifying Entity Associations
Figure 29-12

Figure 29-12. Figure 29-12

This dialog includes:

  • Association Name: Give the association a name — this will become the name of the foreign key constraint in the database if you update the database from the model.

  • Endpoints: These specify the entities at each end of the association, the type of relationship (one-to-one, one-to-many, and so on), and the name of the navigation properties that it will create on both entities to navigate from one entity to the other over the association.

  • Add foreign key properties to the entity: This enables you to create a property on the "foreign" entity that will act as a foreign key and map to the entity key property over the association. If you've already added the property that will form the foreign key on the associated entity, you should uncheck this checkbox.

The other way to create an association is to click the Association component in the Toolbox, click one entity to form an end on the association, and then click another entity to form the other end of the association (if it is a one-to-many relationship, select the "one" entity first). Using this method gives the association a default name, creates the navigation properties on both entities, and assumes a one-to-many relationship. It will not create a foreign key property on the "foreign" entity. You can then modify this association as required using the Properties tool window.

Note

Note that you cannot use the association component in a drag-and-drop fashion from the Toolbox.

Despite having created the association, you aren't done yet (unless you used the first method and also selected the option to create a foreign key property for the association). Now you need to map the property that acts as the foreign key on one entity to the entity key property on the other. The entity whose primary key is one endpoint in the association is known, but you have to tell the Entity Framework explicitly which property to use as the foreign key property. You can do this by selecting the association in the designer and using the Mapping Details tool window to map the properties.

Once this is done, you may want to define a referential constraint for the association, which you can assign by clicking the association in the designer and finding the Referential Constraint property in the Properties tool window.

Entity Inheritance

In the same way that classes can inherit from other classes (a fundamental object-oriented concept), so can entities inherit from other entities. You have a number of ways of specifying that one entity should inherit from another, but the most straightforward method is to select an entity in the designer, find its Base Type property in the Properties tool window, and select the entity from the drop-down list that this entity should inherit from.

Validating an Entity Model

At times your entity model may be invalid (such as when a property on an entity has not been mapped to the storage model, or its type cannot be converted from/to the mapped column's data type in the database); however, despite having an invalid entity model your project will still compile.

You can run a check to see if your model is valid by right-clicking the designer surface and selecting the Validate menu item from the context menu. This checks for any errors in your model and displays them in the Error List tool window.

You can also set the Validate On Build property for the conceptual model to True (click an empty space on the designer surface, and then you can find the property in the Properties tool window), which will automatically validate the model each time you compile the project. However, again, an invalid model will not stop the project from successfully compiling.

Updating an Entity Model with Database Changes

The structure of databases tends to be updated frequently throughout the development of projects, so you need a way to update your model based on the changes in the database. To do so, right-click the designer surface and select the Update Model from Database menu item. This opens the Update Wizard (as shown in Figure 29-13) that will obtain the schema from the database, compare it to the current storage model, and extract the differences. These differences are displayed in the tabs in the wizard — the Add tab contains database objects that aren't in your storage model, the Refresh tab contains database objects that are different in the database from their corresponding storage model objects, and the Delete tab contains database objects that are in the storage model but no longer in the database.

Figure 29-13

Figure 29-13. Figure 29-13

Select the items from these three tabs that you want to add, refresh, or delete, and click the Finish button to have your entity model updated accordingly.

QUERYING THE ENTITY MODEL

Now that you've created your entity model you will no doubt want to put it to the test by querying it, working with and modifying the data returned, and saving changes back to the database. The Entity Framework provides a number of ways to query your entity model, including LINQ to Entities, Entity SQL, and query builder methods. However, this chapter focuses specifically on querying the model with LINQ to Entities.

LINQ to Entities Overview

LINQ was covered in the previous chapter, specifically focusing on the use of LINQ to Objects, LINQ to SQL, and LINQ to XML; however, the Entity Framework has extended LINQ with its own implementation called LINQ to Entities. LINQ to Entities enables you to write strongly typed LINQ queries against your entity model, and have it return the data as objects (entities). LINQ to Entities handles the mapping of your LINQ query against the conceptual entity model to a SQL query against the underlying database schema. This is an extraordinarily powerful feature of the Entity Framework, abstracting away the need to write SQL to work with data in a database.

Getting an Object Context

To connect to your entity model you need to create an instance of the object context in your entity model. So that the object context is disposed of once you're finished you'll use a using block to maintain the lifetime of the variable:

VB
Using context As New AdventureWorksLTEntities()
    'Queries go here
End Using
C#
using (AdventureWorksLTEntities context = new AdventureWorksLTEntities())
{
    // Queries go here
}

Note

Note that any queries placed within the scope of the using block for the object context aren't necessarily executed while the object context is in scope. As detailed in the "Debugging and Execution" section of Chapter 28, the execution of LINQ queries is deferred until the results are iterated (i.e., the query is not run against the database until the code needs to use its results). This means that if the variable containing the context has gone out of scope before you are actually using the results, the query will fail. Therefore, ensure that you have requested the results of the query before letting the context variable go out of scope.

If you need to specify the connection to the database (such as if you need to pass in user credentials or use a custom connection string rather than what's in the App.config file) you can do so by passing the connection string to the constructor of the object context (in this case AdventureWorksLTEntities).

CRUD Operations

It would be hard to argue against the most important database queries being the CRUD (Create/Read/Update/Delete) operations. Read operations return data from the database, whereas the Create/Update/Delete operations make changes to the database. Create some LINQ to Entities queries to demonstrate retrieving some data from the database (as entities), modify these entities, and then save the changes back to the database.

Note

While you are getting up to speed on writing LINQ to Entities queries, you may find LINQPad to be a useful tool, providing a "scratchpad" where you can write queries against an entity model and have them executed immediately so you can test your query. You can get LINQPad from http://www.linqpad.net.

Data Retrieval

Just like SQL, LINQ to Entity queries consist of selects, where clauses, order by clauses, and group by clauses. Take a look at some examples of these. The results of the queries can be assigned to the ItemsSource property of the DataGrid control created earlier in the MainWindow.xaml file, enabling you to visualize the results:

VB
dgEntityFrameworkData.ItemsSource = qry
C#
dgEntityFrameworkData.ItemsSource = qry;

There are actually a number of ways to query the entity model within LINQ to Entities, but we'll just focus on one method here. We'll also assume that the query is between the using block demonstrated previously, with the variable containing the instance of the object context simply called context.

To return the entire collection of customers in the database you can write a select query like so:

VB
Dim qry = From c In context.Customers
          Select c
C#
var qry = from c in context.Customers
          select c;

You can filter the results with a where clause, which can even include functions/properties such as StartsWith, Length, and so on. This example returns all the customers whose last name starts with A:

VB
Dim qry = From c In context.Customers
          Where c.Name.LastName.StartsWith("A")
          Select c
C#
var qry = from c in context.Customers
          where c.Name.LastName.StartsWith("A")
          select c;

You can order the results with an order by clause — in this example you are ordering the results by the customer's last name:

VB
Dim qry = From c In context.Customers
          Order By c.Name.LastName Ascending
          Select c
C#
var qry = from c in context.Customers
          orderby c.Name.LastName ascending
          select c;

You can group and aggregate the results with a group by clause — in this example you are grouping the results by the salesperson, returning the number of sales per salesperson. Note that instead of returning a Customer entity you are requesting that LINQ to Entities returns an implicitly typed variable containing the salesperson and his sales count:

VB
Dim qry = From c In context.Customers
          Group c By salesperson = c.SalesPerson Into grouping = Group
          Select New With
          {
                .SalesPerson = salesperson,
                .SalesCount = grouping.Count()
          }
C#
var qry = from c in context.Customers
          group c by c.SalesPerson into grouping
          select new
          {
              SalesPerson = grouping.Key,
              SalesCount = grouping.Count()
          };

Note

It can be very useful to monitor the SQL queries generated and executed by the Entity Framework to ensure that the interaction between the entity model and the database is what you'd expect. For example, you may find that because an association is being lazy loaded, when traversing the entity hierarchy across this association in a loop that you are actually making repeated and excessive trips to the database. Therefore, if you have SQL Server Standard or higher you can use the SQL Profiler to monitor the queries being made to the database and adjust your LINQ queries if necessary. If you are using SQL Server Express you can download a free open source SQL Server profiler called SQL Express Profiler from http://code.google.com/p/sqlexpressprofiler/downloads/list.

Saving Data

The Entity Framework employs change tracking — where you make changes to data in the model, it will track the data that has changed, and when you request that the changes are saved back to the database it will commit the changes to the database as a batch. This commit is via the SaveChanges() method on the object context:

VB
context.SaveChanges()
C#
context.SaveChanges();

A number of ways to update data exists (for different scenarios), but for purposes of simplicity this example takes the simple straightforward approaches.

Update Operations

Assume you want to modify the name of a customer (with an ID of 1), which you've retrieved like so:

VB
Dim qry = From c In context.Customers
          Where c.CustomerID = 1
          Select c

Dim customer As Customer = qry.FirstOrDefault()
C#
var qry = from c in context.Customers
          where c.CustomerID == 1
          select c;

Customer customer = qry.FirstOrDefault();

All you need to do is modify the name properties on the customer entity you've retrieved, the Entity Framework will automatically track that this customer has changed, and then call the SaveChanges() method on the object context:

VB
customer.Name.FirstName = "Chris"
customer.Name.LastName = "Anderson"

context.SaveChanges()
C#
customer.Name.FirstName = "Chris";
customer.Name.LastName = "Anderson";

context.SaveChanges();

Create Operations

To add a new entity to an entity set, simply create an instance of the entity, assign values to its properties, and then save the changes:

VB
Customer customer = new Customer()
customer.Name.FirstName = "Chris"
customer.Name.LastName = "Anderson"
customer.Name.Title = "Mr."
customer.PasswordHash = "*****"
customer.PasswordSalt = "*****"
customer.ModifiedDate = DateTime.Now
context.Customers.AddObject(customer)

context.SaveChanges()
C#
Customer customer = new Customer();
customer.Name.FirstName = "Chris";
customer.Name.LastName = "Anderson";
customer.Name.Title = "Mr.";
customer.PasswordHash = "*****";
customer.PasswordSalt = "*****";
customer.ModifiedDate = DateTime.Now;
context.Customers.AddObject(customer);

context.SaveChanges();

After the changes are saved back to the database your entity will now have the primary key that was automatically generated for the row by the database assigned to its CustomerID property.

Delete Operations

To delete an entity, simply use the DeleteObject() method on its containing entity set:

VB
context.Customers.DeleteObject(customer)
C#
context.Customers.DeleteObject(customer);

Navigating Entity Associations

Of course, working with data rarely involves the use of a single table/entity, which is where the navigation properties used by associations are very useful indeed. A customer can have one or more addresses, which is modeled in your entity model by the Customer entity having an association with the CustomerAddress entity (a one-to-many relationship), which then has an association with the Address entity (a many-to-one relationship). The navigation properties for these associations make it very easy to obtain the addresses for a customer.

Start by using the query from earlier to return a customer entity:

VB
Dim qry = From c In context.Customers
          Where c.CustomerID = 1
          Select c

Dim customer As Customer = qry.FirstOrDefault()
C#
var qry = from c in context.Customers
          where c.CustomerID == 1
          select c;

Customer customer = qry.FirstOrDefault();

You can enumerate and work with the addresses for the entity via the navigation properties like so:

VB
For Each customerAddress As CustomerAddress In customer.CustomerAddresses
    Dim address As Address = customerAddress.Address
    'Do something with the address entity
Next customerAddress
C#
foreach (CustomerAddress customerAddress in customer.CustomerAddresses)
{
    Address address = customerAddress.Address;
    // Do something with the address entity
}

Note how you navigate through the CustomerAddress entity to get to the Address entity for the customer. Because of these associations there's no need for joins in the Entity Framework.

However, there is an issue here with what you're doing. What is happening here is as you navigate through the CustomerAddress entity to the Address entity is that it's doing another database query to get the collection of CustomerAddress entities for the customer, and then in the for loop doing yet another database query for each CustomerAddress entity to get the corresponding Address entity! This is known as lazy loading — where the entity model only requests data from the database when it actually needs it. This can have some advantages in certain situations, however, in this scenario it results in a lot of calls to the database, increasing the load on the database server, reducing the performance of your application, and reducing your application's scalability. If you then did this for a number of customer entities in a loop, that would add even more strain to the system. So it's definitely not an ideal scenario as is.

Instead, you can request from the entity model when querying for the customer entity that it eagerly loads its associated CustomerAddress entities and their Address entities. This will request all the data in one database query, thus removing all the aforementioned issues, because when navigating through these associations the entity model will now have the entities in memory and not have to go back to the database to retrieve them. The way to request that the model does this is to use the Include method, specifying the path (as a string) of the navigation properties (dot notation) to the associated entities whose data you also want to retrieve from the database at the same time as the actual entities being queried:

VB
Dim qry = From c In context.Customers
                            .Include("CustomerAddresses")
                            .Include("CustomerAddresses.Address")
          Where c.CustomerID = 1
          Select c

Dim customer As Customer = qry.FirstOrDefault()
C#
var qry = from c in context.Customers
                            .Include("CustomerAddresses")
                            .Include("CustomerAddresses.Address")
          where c.CustomerID == 1
          select c;

Customer customer = qry.FirstOrDefault();

ADVANCED FUNCTIONALITY

There's too much functionality available in the Entity Framework to discuss in detail in this chapter, but here's an overview of some of the more notable advanced features available that you can investigate further if you wish.

Updating a Database from an Entity Model

As mentioned earlier, it's possible with the Entity Framework to create an entity model from scratch, and then have the Entity Framework create a database according to your model. Alternatively, you can start with an existing database, but then get the Entity Framework to update the structure of your database based on the new entities/properties/associations that you've added to your entity model. To update the structure of the database based on additions to your model, you can use the Generate Database Wizard by right-clicking the designer surface and selecting the Generate Database from Model menu item.

Adding Business Logic to Entities

Though you are fundamentally building a data model with the Entity Framework rather than business objects, it is possible to add business logic to your entities. The entities generated by the Entity Framework are partial classes, enabling to you extend them and add your own code. This code may respond to various events on the entity, or it may add methods to your entity that the client application can use to perform specific tasks or actions.

For example, you might want to have the Product entity in your AdventureWorksLT entity model automatically assign the value of the SellEndDate property when the SellStartDate property is set (only if the SellEndDate property does not have a value). Alternatively, you may have some validation logic or business logic that you want to execute when the entity is being saved.

Each property on the entity has two partial methods that you can extend: a Changing method (before the property is changed) and a Changed method (after the property is changed). You can extend these partial methods in your partial class to respond accordingly to the value of a property being changed.

Plain Old CLR Objects (POCO)

One of the big complaints with the first version of the Entity Framework was that your entities had to inherit from EntityObject (or implement a set of given interfaces), meaning that they had a dependency on the Entity Framework — which made them unfriendly for use in projects where test-driven development (TDD) and domain-driven design (DDD) practices were employed. In addition, many developers wanted their classes to be persistence ignorant — that is, contain no logic or awareness of how they were persisted.

By default, the entities generated from the Entity Model Data Wizard in the Entity Framework v4 still inherit from EntityObject, but you now have the ability to use your own classes that do not need to inherit from EntityObject or implement any Entity Framework interfaces, and whose design is completely under your control. These types of classes are often termed Plain Old CLR Objects, or POCO for short.

SUMMARY

In this chapter you learned that the Entity Framework is an Object Relational Mapper (ORM) that enables you to create a conceptual model of your database in order to interact with databases in a more productive and maintainable manner. You then went on to learn how to create an entity model, and how to write queries against it in code.

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

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