Chapter 7. Tuning Up a Model

In the previous chapters, we discussed some of the core concepts of the Entity Framework, the Entity Data Model (EDM), querying, and other straightforward operations. The simple database and console application we used illustrated key points and kept you focused on the lessons. Now it’s time to look at some more realistic scenarios.

In this chapter, we’ll create and work with a more realistic model. The model will be contained in its own assembly so that you can reuse it in other applications in your enterprise.

The chapter will also address the important task of clarifying the names used in a model that has been created from a database. You will also learn about many-to-many relationships as well as a few more tips about mapping stored procedures.

The BreakAway Geek Adventures Business Model

The example company for which we will be writing software is called BreakAway Geek Adventures. This small company arranges adventure vacations for hard-working programmers who need a break. Examples of vacations that can be booked through BreakAway Geek Adventures include whitewater rafting in Belize and bicycling in Ireland. The company has been in business for a number of years and has an old application that uses a SQL Server database for its data store. Now it’s time to write shiny new applications for this venerable firm in .NET, leveraging the Entity Framework.

Note

You can download a script for creating this database from the book’s website. Look for the database named BreakAway. There is a version for SQL Server 2005 and a version for SQL Server 2008.

Figure 7-1 shows the BreakAway database schema.

The BreakAway database schema

Figure 7-1. The BreakAway database schema

Creating a Class Library Project to Host an EDM

The first step is to create the new model. Rather than create the EDM directly in the Windows application, in this chapter you will create a separate project for the EDM. This is a good start on your way to planning for larger applications.

  1. In Visual Studio, create a new Class Library project named BreakAwayModel.

  2. Delete the Class1 file that was automatically created.

  3. Add a new ADO.NET EDM to the project. Change the default name (Model1.edmx) to BAModel.edmx.

  4. On the Choose Model Contents page, choose Generate from Database and then select the BreakAway Data Connection if it has already been added to Visual Studio. If it hasn’t been added, create it on the fly using the New Connection button. Leave the default connection settings name, BreakAway, alone for now and go to the next page of the wizard.

  5. On the Choose Your Database Objects page, check all three objects: Tables, Views, and Stored Procedures.

    Open the Tables node. You will see that because the database contains a diagram, the table that controls the diagram is listed (sysdiagrams). Uncheck that since you don’t need the diagram in your model. Creating the diagram in SQL Server Management Studio also resulted in seven stored procedures and one function being added for the sake of diagramming. Their names begin with either fn_ or sp_ and contain the word diagram. They won’t interfere with your model, but you can uncheck these procedures and functions if you prefer.

  6. Leave the default model namespace intact. You’ll get a chance to change that shortly.

  7. Wrap up model creation by clicking the Finish button.

The newly created model will open in the Designer window and should look something like Figure 7-2.

The initial model created from the BreakAway database

Figure 7-2. The initial model created from the BreakAway database

Inspecting and Cleaning Up a New Model

The first thing you should always do with a newly generated model is make sure the Entity names and EntitySet names make sense. The Entity names should be singular (Contact, Address, Customer, etc.) and the EntitySet names should be the plural form of the Entity names (Contacts, Addresses, Customers, etc.). The word equipment poses a challenge since the singular and plural versions are the same. The rule that the Designer follows when you create new entities in the Designer is to append the word Set to the name of the new entity, so we’ll follow that convention and use EquipmentSet in this model. Although you can edit the Entity names right in the Designer, you can edit the EntitySet names only in the Properties window. You may find it more efficient to edit both in the Properties window.

Note

There are three ways to get an entity’s properties to display in the Properties window:

  • Select the entity in the Designer.

  • Select the entity in the Model Browser.

  • Select the entity from the drop-down list of objects in the Properties window.

Modifying the Names of Entities

The database has a table named Events that refers to the trips that BreakAway schedules. The original name of this table was an unfortunate choice because the .NET word Event is a reserved keyword in both VB and C#. This normally isn’t a problem, but if you were to use Event as the entity name, the EntityObject named Event would create a conflict. With the EDM, you can rename the entity without having to rename the database table. The term Trip also makes more sense, so renaming this will be a bonus. As you fix the names of the Entity objects and EntitySets, rename the Events entity to Trip. The entity will still map back to the Events table, so everything will stay in sync.

You should also change the EventID property name to TripID so that as you are working with objects, you won’t have the confusion of an entity whose ID property doesn’t match the name of the entity.

Do the same for the entity named Locations, changing it to Destination. You’ll need to change the ID and Name properties as well, to DestinationID and DestinationName.

Collisions Between Property Names and Entity Names

The Designer will pop up an error message when you try to change the names of two entities. The first is CustomerTypes and the second is Activities. The problem is that the entities contain properties with the same names you would like the entities to have.

Before you can change the CustomerTypes entity name to CustomerType, you will need to rename its CustomerType property. Change the name to CustomerTypeName. Then change the Activity property of the Activities entity to ActivityName. You can make these changes in the Designer or in the Properties window.

You may also notice that the Equipment entity has a property called Equipment1. That is because the wizard saw that the property name matched the entity name and therefore appended a “1” to the property. Change that property to EquipmentName.

Now you will be able to change the names of the entities without problems.

Table 7-1 shows the names that result. If you make these same changes, the queries and other code that follow will work properly.

Table 7-1. Entity and EntitySet name changes

Entity

EntitySet

Activity *Change the Activity property to ActivityName

Activities

Address

Addresses

Contact

Contacts

Customer

Customers

CustomerType *Change the CustomerType property to CustomerTypeName

CustomerTypes

Equipment *Change the Equipment1 property to EquipmentName

EquipmentSet

Trip (was Events)

Trips (was Events)

Destination (was Locations)

Destinations

Lodging

Lodgings

Payment

Payments

Reservation

Reservations

vOfficeAddress

vOfficeAddresses

Warning

You’ll notice some bad behavior by the Designer as you rename some of the entities. When you’re renaming the EntitySets, all is well, but when you rename the entity, the Designer will automatically rename the EntitySet using its pattern for new entities, which is to append the word Set onto the end.

This means that after changing the entity names, you’ll need to reedit the EntitySet names. Thankfully in this model, this will occur with only eight entities.

Cleaning Up Navigation Property Names

There is still a bit of cleaning up to do. Next, you will fix the names of the navigation properties so that they make sense when you are building queries and working with the objects returned from those queries.

Navigation property names should be plural when pointing to a collection (e.g., reservations that a customer has made) and singular when pointing to a reference (the customer who made a reservation).

The Customer entity has some odd navigation properties: Locations and Locations1, and Activities and Activities1. The Activity and Location entities each have Customers and Customers1 in the Activity entity and the Destination entity. Fix the other navigation properties as shown in Table 7-2, and then we’ll come back to these.

Table 7-2. Changes made to navigation names to better represent their multiplicity

Entity

Original navigation property name

Fixed navigation property name

Reason

Contact

Address

Addresses

1:* Navigation

Customers

Customer

1:1 Navigation

Lodging

Lodgings

1:* Navigation

Customer

CustomerTypes

CustomerType

*:1 Navigation

Trip

Locations

Destination

*:1 Navigation and entity name changed

Activity

Events

Trips

Entity name changed

Destination

Lodging

Lodgings

1:* Navigation

Events

Trips

Entity name changed

Lodging

Destinations

Destination

*:1 Navigation and entity name changed

Events

Trips

Entity name changed

Payment

Reservations

Reservation

*:1 Navigation

Reservation

Customers

Customer

*:1 Navigation

Events

Trip

*:1 Navigation and entity name changed

As you write queries against this model going forward, the queries will be more logical. Rather than coding against Reservation.Customers, when there will always be just one customer, you will be able to use Reservation.Customer.

Entities with Multiple Relationships to a Single Entity

Now we can go back to work on the Customer entity. It has those two funny pairs of navigation properties: Activities and Activities1, and Locations and Locations1. These property pairs will make more sense if you check the Customers table in the database, shown in Figure 7-3. BreakAway keeps track of each customer’s first and second preferences for destination and activity. This is not an uncommon database scenario, but the wizard will always create the names in this way, so let’s see how to add clarity to these names.

The Customer table in the database, with two columns that relate to the Destination table (PrimaryDesintation—a database typo that BreakAway developers have had to live with for years; and SecondaryDestination) and two columns that point to the Activities table

Figure 7-3. The Customer table in the database, with two columns that relate to the Destination table (PrimaryDesintation—a database typo that BreakAway developers have had to live with for years; and SecondaryDestination) and two columns that point to the Activities table

The navigation property names are derived simply from the name of the table on the other end of the relationship. Since there are two associations to one entity, the wizard appended a “1” to the second navigation property.

Note

Did you notice that the PrimaryDestination column was misspelled in the database? In the previous application, the developer had to constantly tangle with this field name. But with the EDM it will no longer be a problem. Though a small detail, this is a really nice benefit of using the data model. Changing the field name in the database could have a big impact in the database schema, especially if that field name is used in views, functions, or stored procedures. In the model, you can change the property to whatever name you like without impacting the database.

Determining Which Navigation Property Is Mapped to Which Foreign Key Field

Before you can rename these navigation properties, you’ll need to figure out which foreign key fields the navigation properties belong to. For example, does Customer.Activities refer to the PrimaryActivity or the SecondaryActivity?

You can do this by looking at the properties of each navigation property and seeing which association it is bound to, and then looking at that association and seeing which field is involved.

Let’s start with Activities. Click the Activities navigation property in the Customer entity. In its Properties window, BreakAway.FK_Customers_Activities is the Association property.

Use the Properties window drop-down (near the top of the Properties window) to select that association.

Note

There are a number of ways to select an association in the model. The Properties window drop-down is one way to select the association. You can also select it in the Model Browser. An additional method is to right-click a navigation property and to choose Select Association from its context menu. Any of these methods will cause the association to be highlighted in the Designer and its properties to display in the Properties window.

Right-click the association’s line in the model and select Table Mapping from the context menu. The term Table Mapping seems to be used generically in the Designer, even when the mapping is an Association Mapping. In Figure 7-4, you can see that this association is for the PrimaryActivity.

Checking the mapping details of an association to discover which foreign key is involved in the association

Figure 7-4. Checking the mapping details of an association to discover which foreign key is involved in the association

Rename the Activities navigation property to PrimaryActivity and the Activities1 navigation property to SecondaryActivity.

You can do the same detective work for the Locations and Locations1 navigation properties to see which one should be named PrimaryDestination and which one should be named SecondaryDestination.

You need to fix the other ends of these associations as well. The Activity entity has two navigations back to the Customer entity. Going in this direction, the navigations represent “Customers who have listed this activity as their primary activity” and “Customers who have listed this activity as their secondary activity.” Rename Customers to PrimaryPrefCustomers and Customers1 to SecondaryPrefCustomers. Make the same changes to the Customers and Customers1 navigation properties in the Destination entity.

Mapping a Few Stored Procedures

The database has a number of stored procedures. For now, we’ll do function mapping for the procedures for the Payments table—InsertPayment, UpdatePayment, and DeletePayment—using the same technique you learned in Chapter 6.

Open the Stored Procedure Mappings window for the Payment entity and select the appropriate functions for insert, update, and delete.

Mapping the Insert Function

The InsertPayment function returns a newly generated PaymentID called NewPaymentID. The parameter names don’t match the property names of the entity; therefore, you will need to manually map some of the properties.

Notice that the InsertPayment function needs to know the ReservationID. In the model, Reservation is a navigation property of the Payment entity. You will have access to the navigation property in the mapping window, so you can select Reservation.ReservationID to map to the required parameter.

Be sure to map that to the Result Column Bindings item, as you did for the InsertContact function in the preceding chapter. The insert mapping should look the same as in Figure 7-5.

Mapping the input parameters and the results of the InsertPayment stored procedure to properties in the Payment entity

Figure 7-5. Mapping the input parameters and the results of the InsertPayment stored procedure to properties in the Payment entity

Mapping the Update Function

When you are mapping the UpdatePayment function, you will also have to manually map the properties whose names don’t match the input parameters.

Using the Use Original Value checkbox

Because of the way this stored procedure works, you can take advantage of the special Use Original Value column that exists only for the update functions. The stored procedure performs a concurrency check against the timestamp field. If anyone edited the record in between the time the user retrieved the record and when he attempted to save changes, the order won’t be updated and an OptimisticConcurrencyException will be thrown. You’ll learn more about working with concurrency in Chapter 18.

When a payment is updated, the database will automatically update the timestamp field. The UpdatePayment procedure returns the new timestamp value. Map that return value as shown in Figure 7-6.

The UpdatePayment function mapping

Figure 7-6. The UpdatePayment function mapping

The DeleteFunction mapping and an awkward stored procedure parameter

A typical stored procedure for deleting a record takes the primary key field as a parameter. When you map the Delete function you will see that the DeletePayment function has not only the PaymentID as a parameter, but also ReservationID.

This is in the database in advance to simplify your task of mapping the functions, but you will not typically have the additional parameter. Why is it there and why should it make the mapping simpler? A mapping requirement with the Entity Framework makes it necessary.

In the Insert and Update stored procedures, it makes sense to have the ReservationID as a parameter because that supplies the ForeignKey value that the Payment record in the database needs.

As a general rule, the Entity Framework requires properties that are involved in association mappings to be mapped in all of the function mappings for the entity. Because Payment has an association to Reservation—even in the function where it seems illogical, Delete—you must map the property that the association revolves around (Reservation.ReservationID). This is not meant to aggravate you or your database administrator, but it is how the Entity Framework is able to generically deal with all of the things you might try to achieve in your models and your queries. So, unfortunately, developers are stuck with the rule regardless of whether it makes sense to them. Putting the extra parameter(s) into the stored procedure in the database is the easiest way to deal with this.

If that’s not an option in your application, the alternative is to modify your model’s Store Schema Definition Layer (SSDL). You will learn in Chapter 13 that it’s possible to modify the SSDL to create virtual tables, stored procedures, and functions that don’t physically exist in the database. The downside to modifying the SSDL manually is that if you update the model, many of the SSDL modifications will be overwritten.

At this stage in the book, it is simpler to just have the parameter written directly into the stored procedure. Map the DeletePayment function so that it matches Figure 7-7.

The DeletePayment function mapping

Figure 7-7. The DeletePayment function mapping

You now have an Entity Data Model from a highly normalized database. Since you have already done so much work on this model, we will leave the task of performing more advanced customizations to Chapters 12 and 13. Figure 7-8 shows the current view of the model, with some of the entities moved around to make it more visually appealing.

The new BreakAway model

Figure 7-8. The new BreakAway model

Working with Many-to-Many Relationships

There is one more thing to point out about this model: the two many-to-many relationships. BreakAway Adventures’ database keeps track of which type of equipment is needed for which activities. It also tracks which activities will be available on which events (“trips” in the model). To accomplish this, an ActivityEquipment join table between Equipment and Activities defines many-to-many relationships between equipment and activities, and an EventActivities join table between Activities and Events defines many-to-many relationships between activities and events, as shown in Figure 7-9.

The database join tables, EventActivities and ActivityEquipment

Figure 7-9. The database join tables, EventActivities and ActivityEquipment

These tables did not appear in the model as entities. The EDM has the ability to represent many-to-many relationships while hiding the join in the mappings. But it can do this only when the join table has just the relevant keys and no extraneous fields. These two tables meet that criterion, as they have only the IDs of the items they are joining. If the join tables had additional properties, such as DateCreated, the EDM would have created entities for them.

Instead, the joins are controlled in mappings; in the conceptual layer the relationships are expressed as navigation properties. Example 7-1 shows the mapping for the EventActivities association in the XML file. The mapping identifies the EventActivities table as the target of the mapping, and then shows its ActivityID field wired up to the ActivityID field of the EventActivities table. Meanwhile, its EventID field is wired up to the EventID field of the Events table.

Example 7-1. Many-to-many association mapping

<AssociationSetMapping Name="EventActivities"
                       TypeName="BreakAway.EventActivities"
                       StoreEntitySet="EventActivities">
  <EndProperty Name="Activities">
    <ScalarProperty Name="ActivityID" ColumnName="ActivityID" />
  </EndProperty>
  <EndProperty Name="Events">
    <ScalarProperty Name="EventID" ColumnName="EventID" />
  </EndProperty>
</AssociationSetMapping>

As you can see in Figure 7-10, Activity and Equipment are joined in a many-to-many relationship. Thanks to the original table names, the navigation property names happen to be just right and don’t need editing. Each piece of equipment has activities and each activity has a collection of equipment.

Activity and Equipment joined in a many-to-many relationship

Figure 7-10. Activity and Equipment joined in a many-to-many relationship

Each activity also has trips and every trip has a collection of activities. It will be very convenient not to have to construct joins when traversing these relationships in queries. Because the join tables contain only the keys involved, the EDM can easily represent the relationship without the aid of a join entity.

This mapping not only enables a convenient association directly between the two entities, but also manages querying, inserts, and updates across this join. You’ll see this in action as you move throughout the book.

Building the BreakAwayModel Project

Now it’s time to build the model into an assembly that you will be able to use in the many projects that you will be building in upcoming chapters.

Don’t Overlook the Assembly and Model Names

Before you compile the model, you will want to change a few names so that when you access the model and its classes from another project, you won’t have to work with cumbersome names.

BAGA assembly namespace

You will have to make references to the assembly namespace throughout the code of your other applications that are using that namespace. Therefore, it will be handy to have a nice, short name for the namespace. The acronym for BreakAway Geek Adventures is BAGA, which is a good option.

Open the project’s Properties window, and on the first page, Application, change the root namespace (VB)/default namespace (C#) to BAGA.

Entity container name

When you created the model with the Entity Data Model Wizard, you left the default name for the EntityContainer as BreakAway. Change that name to BAEntities. Remember that the place to do this is in the model’s Properties window, which you can access when the model is open in the Designer.

Note

When you change this name and save the model, the ConnectionString name in the app.config file should change to BAEntities as well. It’s not a bad idea to double-check that this happened by looking in the app.config file.

Changing this name will make typing Entity SQL expressions easier, as you will have to include this container name in every Entity SQL expression.

Model namespace

You can change the model’s namespace so that it’s consistent with the container name, in this case to BAModel.

The Impact of Compiling a Project on an EDMX File

When a project containing an EDMX is compiled, the compiler extracts the StorageModels, ConceptualModels, and Mappings sections of the EDMX file and creates individual schema files from them. In this case, the files are BAModel.ssdl, BAModel.csdl, and BAModel.msl. By default, these files are embedded into the assembly that is built from the project.

Figure 7-11 shows the compiled assembly in Red Gate’s Reflector tool, with the embedded files listed under Resources.

The schema files embedded in the assembly by default

Figure 7-11. The schema files embedded in the assembly by default

If you look at the metadata portion of the EntityConnection string that the Entity Data Model Wizard inserted into the app.config file, you’ll see the following notation:

res://*/BAModel.csdl|res://*/BAModel.ssdl|res://*/BAModel.msl

Much of the functionality in the Entity Framework depends on its ability to read the schema files. The * in the metadata of the connection string indicates that you can find the files in the assembly.

Splitting out the schema files

Having the model in the assembly is convenient when you don’t expect the model to change often after it has been deployed. However, you may want to take advantage of the model’s loose coupling at some point. For example, you or your database administrator might modify the database in a way that changes the schema, but introduces nothing new that would impact the objects in the application. In this case, you would need to update the model so that the database changes are reflected in the SSDL schema. Then, because of this change, you would need to adjust some of the mappings to be sure that the entities are mapped correctly to the SSDL. So, in this scenario, the SSDL and MSL layers change, but no change is made to the conceptual layer.

You may not want to have to rebuild and redeploy the assembly. Doing so may also affect the versioning of your application.

Although the files are embedded by default, there is an option to have the files exist outside the assembly. The model has a property called Metadata Artifact Processing. The property is available in the model’s Properties window, as shown in Figure 7-12.

Changing how the model’s schema files are created during the build process

Figure 7-12. Changing how the model’s schema files are created during the build process

Notice that the connection string has changed. The metadata no longer has a * to indicate that the files are embedded. Instead, it shows the relative path of the files. You will find them in the project’s output directory, which by default is in either the bindebug or the bin elease folder in the project folder.

Moving the schema files

You can put the schema files anywhere you want. However, you will need to be sure that the connection string points to the correct path. If, for example, you place the files in C:EDMS, you’ll need to modify the metadata attribute to the following:

metadata=C:EDMSBAModel.csdl| C:EDMSBAModel.ssdl| C:EDMSBAModel.msl

Note

Although this chapter covered creating a model in a separate assembly, it’s useful to be aware of a special case for the metadata attribute. If you create an EDM inside an ASP.NET Web Site Project, because of the way in which Web Site Projects are compiled, the path will be affected. The entire metadata attribute will be metadata=res://*. This does not happen with Web Application Projects.

You can learn more about the EntityConnection’s metadata attribute in the MSDN Library documentation.

Summary

In this chapter, you went through the steps of creating an EDM from a more realistic database, which you will be using throughout the rest of this book. Then you spent some time cleaning up many of the automatically created entity and property names so that they will be more logical when it comes time to use the model in your applications.

You have now prepared an assembly that can easily be referenced from a variety of projects and used in other applications. Because the runtime schema files are embedded into the assembly, it will be even simpler to reuse and share the model.

In the next chapter, you will write your first Windows applications using this model.

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

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