Chapter 39. Manipulating Databases with the Entity Framework

I have seen estimates claiming that 80% or more of all Visual Basic applications involve a database. I've even seen one estimate that claims 95% of all Visual Basic applications involve a database at some point. Whatever the true number is, it's clear that a large fraction of Visual Basic programs use databases.

There are some good reasons for this. Databases provide a lot of useful features for storing, manipulating, updating, and retrieving data. A well-designed database can store huge amounts of data and still find a particular record quickly.

With such a strong emphasis on databases, it's small wonder that Microsoft spends a lot of time and effort providing tools to manipulate databases, and over the years Microsoft has developed many such tools. The latest of these is the ADO.NET Entity Framework.

In this lesson, you learn the basics of using the Entity Framework. You learn how to use it to map data in a database to objects in a program. You also learn how to build a simple program that uses the objects created by the Entity Framework to manipulate the data in the database.

ENTITY FRAMEWORK FEATURES

Although a detailed discussion of the Entity Framework is beyond the scope of this book, this section explains its general concepts and key features.

In brief, the Entity Framework is an object-relational mapper (abbreviated ORM, O/RM, or O/R mapper), a tool that maps between pieces of data in a relational database and objects in a program. That mapping enables you to program using objects such as Person, Customer, and Order without worrying about how those objects are stored in the database; and the ORM automatically loads and saves data as needed.

For example, suppose an Order object defines an OrderedBy property that refers to the Customer who placed an order. When the program fetches an Order object from the database, the ORM automatically loads the OrderedBy property without the program needing to know how that happens.

Some of the more important features provided by the Entity Framework include the following:

  • Generating classes from a database — The Entity Framework can automatically generate classes to hold the data contained in some or all of a database's tables. It can even give classes properties that link tables together.

  • Generating classes from a model — After you design an entity data model, the designer automatically generates classes to hold the data in the model.

  • Model-first development — You can build a data model first and then use the Entity Framework to build an empty database for it.

  • Lazy loading — If an Order object has a collection of OrderItems, the Entity Framework Order class can defer loading the OrderItems until they are actually needed.

USING THE ENTITY FRAMEWORK: A BASIC APPROACH

Following is one basic approach to using the Entity Framework:

  1. Build a SQL Server database containing the data.

  2. Use the Entity Framework to make a data model and generate classes.

  3. Use the Entity Framework classes to manipulate data.

Rather than explain these steps in excruciating detail that won't make much sense to you until you actually try it yourself, the following sections walk you through these steps so you can see how they work. Basically, they form an extra-detailed walk-through similar to a Try It's Step-by-Step instructions.

To follow along you'll need the following:

  • Microsoft Visual Studio 2010 (which you should have if you're reading this book)

  • The .NET Framework 4 (which you should also have if you're reading this book)

  • Microsoft SQL Server 2005 or later, with Database Services or Microsoft SQL Server 2005 Express Edition or later

If you don't meet these requirements — in particular, if you don't have SQL Server installed — you can skim the rest of this lesson but you won't be able to work through the Try It or Exercises.

You may also want to install SQL Server Management Studio for use in building and manipulating databases. You can download SQL Server Management Studio Express from Microsoft's web site.

Building a SQL Server Database

If you haven't installed SQL Server, you might want to do so now. The Visual Studio installation software comes with SQL Server (at least the versions I've seen) or you can download the free SQL Server Express edition at www.microsoft.com/express/Database. You should probably download the management tools, too. You will find SQL Server Management Studio Express particularly useful for building databases.

Note

If you need information about SQL Server and how to use it, there are many good books available, such as Robert Vieira's Beginning Microsoft SQL Server 2008 Programming (Wrox, 2009) and Professional Microsoft SQL Server 2008 Programming (Wrox, 2009).

You might also be interested in my book Beginning Database Design Solutions (Wrox, 2008), which explains how to design a database that is robust and flexible.

Assuming you have SQL Server installed, you need to create the database that your program will manipulate. To do that:

  1. Start Microsoft SQL Server Management Studio Express.

  2. When the program starts, it asks you to connect to the server that you will use. Enter the appropriate information and click Connect. Figure 39-1 shows the connect dialog that appears when I open my SQL Server Express server named Gothmog.

    Figure 39-1

    Figure 39.1. Figure 39-1

  3. After you connect to the server, the Object Explorer should display the server's objects as shown in Figure 39-2. In this figure you can see the OrdersDatabase and its Customer, OrderItem, and PurchaseOrder tables.

Figure 39-2

Figure 39.2. Figure 39-2

You can use SQL Server Management Studio to build the database interactively. See the program's help for details.

Alternatively, you can execute a SQL script that contains commands to build the database for you. The file MakeOrdersDatabase.sql, which is available in this lesson's download, builds a database called OrdersDatabase that contains Customer, PurchaseOrder, and OrderItem tables.

To run the script, download it from the book's web site. In SQL Server Management Studio, open the File menu, select Open, and pick File; or click [Ctrl]+O. Browse to the script and click Open. After you load the script, execute it by opening the Query menu and selecting Execute, or by pressing [f5].

The following code shows the parts of the script that create the database and its tables:

-- Create and use the OrdersDatabase.
CREATE DATABASE OrdersDatabase;
GO
USE OrdersDatabase;

-- Create the Customer table.
CREATE TABLE Customer
(
    CustomerId        int                PRIMARY KEY,
    FirstName         nvarchar(50)       NOT NULL,
    LastName          nvarchar(50)       NOT NULL,
)

-- Create the PurchaseOrder table.
CREATE TABLE PurchaseOrder
(
    OrderId           int                PRIMARY KEY,
    OrderDate         datetime           NOT NULL,
    CustomerId        int                NOT NULL
        FOREIGN KEY REFERENCES Customer (CustomerId),
)

-- Create the OrderItem table.
CREATE TABLE OrderItem
(
    OrderId           int                NOT NULL
        FOREIGN KEY REFERENCES PurchaseOrder (OrderId),
    ItemNumber        int                NOT NULL,
    ItemDescription   nvarchar(50)       NOT NULL,
    Quantity          int                NOT NULL,
    CONSTRAINT PK_OrderItem PRIMARY KEY (OrderId, ItemNumber)
)

You don't need to understand every detail of this script. You should be able to get the gist of it even if you're unfamiliar with the Transact-SQL database language that it uses.

The CREATE DATABASE statement creates the OrdersDatabase inside the server.

The CREATE TABLE Customer statement creates the Customer table and gives it the fields CustomerId, FirstName, and LastName. CustomerId is an integer, and the table's primary key. FirstName and LastName are strings with lengths up to 50. The NOT NULL clause means all records must have these values filled in. (Primary keys must also always have values.)

The CREATE TABLE PurchaseOrder statement similarly creates the PurchaseOrder table. The only new concept used by this statement is the FOREIGN KEY clause, which indicates that this table's CustomerId field references the Customer table's CustomerId field. That means any record in the PurchaseOrder table must have a CustomerId value that is already present in the Customer table. The idea is that a PurchaseOrder record's CustomerId indicates the ID of the customer who placed the order.

Finally, the CREATE TABLE OrderItem statement creates the OrderItem table. Its FOREIGN KEY clause requires that its OrderId field match a value in the PurchaseOrder table. The idea here is that an OrderItem record's OrderId value indicates which PurchaseOrder includes the OrderItem.

This last CREATE TABLE statement also uses a PRIMARY KEY clause to indicate that the table's primary key includes the combination of OrderId and ItemNumber. Each order may contain several items, and their ItemNumber values indicate the order in which they should appear in the purchase order.

You'll see a graphic representation of this database design in the next section.

Note

This script tries to create the database without checking whether you already have a database named OrdersDatabase, so if one already exists it fails without damaging your existing database.

In addition to creating the OrdersDatabase and its Customer, PurchaseOrder, and OrderItem tables, the script inserts records in the tables to give your programs some data to manipulate. That code is long and not very interesting so it isn't shown here.

Making the Data Model and Classes

To make the data model, start a new Visual Basic Windows Forms project. Then open the Project menu and select Add New Item. On the Add New Item dialog, shown in Figure 39-3, pick the Data category on the left and select ADO.NET Entity Data Model. Enter a name for the model (such as OrdersModel.edmx) and click Add to launch the Entity Data Model Wizard.

Figure 39-3

Figure 39.3. Figure 39-3

The wizard's first page, which is shown in Figure 39-4, lets you decide whether you want to generate a model from a database or create an empty model. An empty model is useful if you want to build the model from scratch and then use Entity Framework tools to build a database from it.

For this example, pick "Generate from database" and click Next to display the Choose Your Data Connection page shown in Figure 39-5. If you have previously defined database connections (which you probably haven't at this point), you can select one from the drop-down list.

Figure 39-4

Figure 39.4. Figure 39-4

Figure 39-5

Figure 39.5. Figure 39-5

If you have no previously defined connections, click New Connection to display the Choose Data Source dialog shown in Figure 39-6. Select the type of data source that you want to use and click Continue. In Figure 39-6, I selected a database server, but the documentation says that Visual Studio Express Edition can select only database files, not servers, so you might need to make a different selection.

Figure 39-6

Figure 39.6. Figure 39-6

When you click Continue, the Connection Properties dialog shown in Figure 39-5 appears. Fill in the server name and database name. Note that you must append SQLEXPRESS to the server name if you are using SQL Server Express Edition. If you don't, you'll get a confusing error message saying, "The server was not found or was not accessible."

When you finish entering the server and database names, click OK to return to the Entity Data Model Wizard page shown in Figure 39-5. Click Next to see the page shown in Figure 39-8. Select all of the tables and click Finish to close the wizard.

Figure 39-7

Figure 39.7. Figure 39-7

Figure 39-8

Figure 39.8. Figure 39-8

At this point the wizard builds the data model and displays it graphically, as shown in Figure 39-9. In addition to building a pretty picture of the model, the wizard builds classes to represent the data. If you look closely at this figure, you can learn about those classes and how they are related.

Figure 39-9

Figure 39.9. Figure 39-9

Note

If you close the data model editor shown in Figure 39-9, you can reopen it by double-clicking on the .edmx file in Solution Explorer.

Each class is represented by a box that lists the properties built by the wizard to correspond to the database table's fields. At the bottom of each class is a list of navigation properties that your program can use to link one class to others. For example, the Customer class's PurchaseOrders property is a collection of PurchaseOrder objects.

If you click on a property in the model, the Properties window displays information about that property such as its name, whether it is nullable (allows null or missing values), and its data type.

The links connecting the classes shown in Figure 39-9 indicate the relationships among the classes. The numbers at either end of a link indicate the numbers of objects from the corresponding classes that are involved in the link.

For example, the link between the Customer and PurchaseOrder classes in Figure 39-9 has a 1 at the Customer end and a * at the PurchaseOrder end. That means one Customer object corresponds to any number of PurchaseOrder objects. Similarly, one PurchaseOrder object corresponds to any number of OrderItem objects.

If you click a link, the Properties window provides information about the link such as the names of the fields in the two tables it connects and their multiplicities.

USING THE ENTITY FRAMEWORK CLASSES

The model displayed in Figure 39-9 shows the main classes that represent database tables: Customer, PurchaseOrder, and OrderItem. There's one more key class that the Entity Data Model Wizard creates to represent the database as a whole: OrdersDatabaseEntities. This class is named after the database (OrdersDatabase) with the word "Entities" added to the end.

To use the classes, create a new instance of the OrdersDatabaseEntities class.

Note

Because the entities object provides a context for manipulating the data, programmers often add "Context" to the name of that class's instance. For example, the following code creates an instance of the OrdersDatabaseEntities class named OrderContext:

' Create the database context.
Private OrderContext As New OrdersDatabaseEntities()

After you create the entities object, you can use its properties to access objects representing the data in the database. In this example, the OrdersDatabaseEntities object's Customers, PurchaseOrders, and OrderItems collections let you manipulate the data.

Note that you can use LINQ as described in Lesson 38 to filter and arrange the data. For example, you could use a LINQ query to select Customer objects with certain FirstName and LastName values and then display the results in a list.

There are two final steps you can take to make the program more useful:

  1. Call the context object's SaveChanges method to save any changes that you have made to the data. For example, if you add, modify, or delete data objects, calling SaveChanges will copy those changes to the database.

  2. When the program is done with the database, it should call the context object's Dispose method to free its resources.

Microsoft's "Getting Started (Entity Framework)" page at http://msdn.microsoft.com/en-us/library/bb386876 is a good starting point for learning more about the Entity Framework.

TRY IT

In this Try It, you add code to the OrdersDatabase example described so far in this lesson. You build the program shown in Figure 39-10 to display the orders placed by a customer and the items contained in each order.

Figure 39-10

Figure 39.10. Figure 39-10

Note

You can download the code and resources for this Try It from the book's web page at www.wrox.com or www.vb-helper.com/24hourvb.html. You can find them in the Lesson39 folder of the download.

Lesson Requirements

In this lesson:

  • Build the OrdersDatabase described in this lesson.

  • Build the user interface shown in Figure 39-10. The two large controls displaying order and order item data are DataGridView controls.

  • Make a CustomerWithName class to hold a Customer object and its full name. Override its String method so the ComboBox at the top in Figure 39-10 can display the customer's full name.

  • When the program starts, create the data context object. Use LINQ to select the Customer objects and make CustomerWithName objects from them. Display them in the ComboBox.

  • When the user selects a customer, display the customer's orders in the first DataGridView.

  • When the user selects an order from the first DataGridView, display its order items in the second DataGridView.

  • Make the Save button save the data.

Hints

  • You can make a DataGridView display a collection of data by setting its DataSource property to the collection. It will figure out what rows and columns to display.

  • Watch out for customers with no orders or orders with no items.

  • To display CustomerWithName objects in the ComboBox, set the control's DataSource property to an array of CustomerWithName objects. Then set its DisplayMember property to FullName, the name of the property that you want the control to display.

  • Hide DataGridView columns that are used only for navigation so the user doesn't see them, as in the following code:

    dgvCustomerOrders.Columns("CustomerId").Visible = False

Step-by-Step

  • Build the OrdersDatabase described in this lesson.

    1. Follow the instructions earlier in this lesson to build the SQL Server database. Download and use the MakeOrdersDatabase.sql script if you like.

  • Build the user interface shown in Figure 39-10. The two large controls displaying order and order item data are DataGridView controls.

    1. This is reasonably straightforward. Make the ComboBox, Button, and two DataGridView controls as shown in Figure 39-10.

  • Make a CustomerWithName class to hold a Customer object and its full name. Override its String method so the ComboBox at the top in Figure 39-10 can display the customer's full name.

    1. Use code similar to the following:

      Public Class CustomerWithName
          Public Customer As Customer
          Public FullName As String
          Public Overrides Function ToString() As String
              Return FullName
          End Function
      End Class
  • When the program starts, create the data context object. Use LINQ to select the Customer objects and make CustomerWithName objects from them. Display them in the ComboBox.

    1. Use code similar to the following:

      ' The database context.
      Private OrderContext As OrdersDatabaseEntities
      
      ' Load the data.
      Private Sub Form1_Load() Handles MyBase.Load
          ' Load the data.
          OrderContext = New OrdersDatabaseEntities()
      
          ' Select the Customers.
          Dim customersQuery =
              From cust As Customer In OrderContext.Customers
              Order By cust.FirstName, cust.LastName
              Select New CustomerWithName With
              {
      .Customer = cust,
                  .FullName = cust.FirstName & " " & cust.LastName
              }
      
          ' Bind the Customers to the cboCustomer ComboBox.
          cboCustomers.DisplayMember = "FullName"
          cboCustomers.DataSource = customersQuery.ToArray()
      End Sub
  • When the user selects a customer, display the customer's orders in the first DataGridView.

    1. Give the ComboBox a SelectedIndexChanged event handler similar to the following:

      ' Display this customer's orders.
      Private Sub cboCustomers_SelectedIndexChanged() _
       Handles cboCustomers.SelectedIndexChanged
          If cboCustomers.SelectedItem Is Nothing Then Exit Sub
      
          ' Get the selected Customer.
          Dim custWithName As CustomerWithName =
              DirectCast(cboCustomers.SelectedItem, CustomerWithName)
          Dim cust As Customer = custWithName.Customer
      
          ' Display this customer's orders.
          dgvCustomerOrders.DataSource = cust.PurchaseOrders
      
          ' Hide navigation properties.
          dgvCustomerOrders.Columns("CustomerId").Visible = False
          dgvCustomerOrders.Columns("Customer").Visible = False
          dgvCustomerOrders.Columns("OrderItems").Visible = False
      
          ' Size the columns.
          dgvOrderItems.AutoResizeColumns()
      
          ' Select the first order (if there is one).
          If dgvCustomerOrders.Rows.Count > 0 Then
              dgvCustomerOrders.Rows(0).Selected = True
          End If
      End Sub
  • When the user selects an order from the first DataGridView, display its order items in the second DataGridView.

    1. Give the ComboBox a SelectedIndexChanged event handler similar to the following:

      ' Display this order's items.
      Private Sub dgvCustomerOrders_SelectionChanged() _
       Handles dgvCustomerOrders.SelectionChanged
          If dgvCustomerOrders.SelectedRows.Count = 0 Then Exit Sub
      
          ' Display the selected purchase order's items.
          Dim po As PurchaseOrder = DirectCast(
              dgvCustomerOrders.SelectedRows(0).DataBoundItem,
              PurchaseOrder)
      ' Display this order's items.
          If po Is Nothing Then
              ' There are no items to display.
              dgvOrderItems.DataSource = Nothing
          Else
              ' Display this order's items.
              dgvOrderItems.DataSource = po.OrderItems
      
              ' Hide navigation properties.
              dgvOrderItems.Columns("PurchaseOrder").Visible = False
              dgvOrderItems.Columns("OrderId").Visible = False
      
              ' Size the columns.
              dgvOrderItems.AutoResizeColumns()
          End If
      End Sub
  • Make the Save button save the data.

    1. Use code similar to the following:

      ' Save changes to the data.
      Private Sub btnSave_Click() Handles btnSave.Click
          Try
              OrderContext.SaveChanges()
              MessageBox.Show("Changes Saved", "Changes Saved",
                  MessageBoxButtons.OK, MessageBoxIcon.Information)
          Catch ex As Exception
              Dim msg As String = ex.Message
              If ex.InnerException IsNot Nothing Then
                  msg &= vbCrLf & vbCrLf & "***** Inner Exception *****" &
                      vbCrLf & ex.InnerException.Message
              End If
              MessageBox.Show(msg)
          End Try
      End Sub

Note

Please select Lesson 39 on the DVD to view the video that accompanies this lesson.

EXERCISES

  1. The Try It uses the CustomerWithName class to enable its ComboBox to display customer first and last names while also storing a reference to the corresponding Customer object. This is somewhat awkward and makes the program convert back to a Customer object when the user selects a new CustomerWithName object.

    A more elegant solution is to override the ToString method in the Customer class so you can display Customer objects directly in the ComboBox. Because the Customer class is automatically generated, you don't really want to mess with its code. However, you can add new methods to the class by using the Partial keyword. That keyword tells Visual Basic that you are defining a class, but part of the class is defined elsewhere.

    The following code adds the necessary ToString override to the Customer class:

    Partial Public Class Customer
        Public Overrides Function ToString() As String
            Return FirstName & " " & LastName
        End Function
    End Class

    Copy the solution you built for the Try It and add this code. Delete the CustomerWithName class and modify the program to work directly with Customer objects.

  2. Copy the program you built for Exercise 1 and add a New Customer button. When the user clicks the button, display a dialog in which the user can enter first name, last name, and customer ID values. If the user clicks OK, create a new Customer object and add it to the context object's Customers collection by calling its AddObject method. Then call the context object's SaveChanges method.

    Hint: To show the new customer, move the code that uses LINQ to build the customer list into a new subroutine and call it after you add the new customer.

  3. Build a program similar to the one you built for Exercise 1 to display student test scores. The Student table should have fields FirstName, LastName, and StudentId. The TestScore table should have fields StudentId, TestNumber, and Score. You can download the MakeTestScoresDatabase.sql script from the book's web page to make building the database easier.

    The program should display the students' names in a ComboBox. When the user selects a student, it should display that student's scores in a DataGridView.

  4. Copy the program you built for Exercise 3 and add two RadioButtons to the form. Label them All Students and Failing Students. Move the code that loads the ComboBox into a DisplayStudents subroutine and make it check the RadioButtons to see what LINQ query to use in selecting the students.

    Give the Student class a new Average function that uses LINQ to return the student's test score average. When the Failing Students RadioButton is checked, make the DisplayStudents subroutine use the following LINQ query:

    Dim failingStudentsQuery =
        From stu As Student In ScoresContext.Students.ToArray()
        Order By stu.FirstName, stu.LastName
        Where stu.Average() > 60
        Select stu

    Note

    You can find solutions to this lesson's exercises in the Lesson39 folder inside the download available on the book's web site at www.wrox.com or www.vb-helper.com/24hourvb.html.

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

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