The wrox.com
code downloads for this chapter are found at www.wrox.com/go/beginningvisualc#2015programming
on the Download Code tab. The code is in the Chapter 21 download and individually named according to the names throughout the chapter.
The previous chapter introduced LINQ (Language-Integrated Query) and showed how LINQ works with objects and XML. This chapter teaches you how to store your objects in a database and use LINQ to query the data.
A database is a persistent, structured storehouse for data. There are many different kinds of databases, but the most common type you will encounter for storing and querying business data is relational databases such as Microsoft SQL Server and Oracle. Relational databases use the SQL database language (SQL stands for Structured Query Language) to query and manipulate their data. Traditionally, working with such a database required knowing at least some SQL, either embedding SQL statements in your programming language or passing strings containing SQL statements to API calls or methods in a SQL-oriented database class library.
Sounds complicated, doesn't it? Well, the good news is that with Visual C# 2015 you can use a Code First approach to create objects in C#, store them in a database, and use LINQ to query the objects without having to use another language such as SQL.
To run the examples shown in this chapter, you must install Microsoft SQL Server Express, the free lightweight version of Microsoft SQL Server. You will use the LocalDB option with SQL Server Express, which enables Visual Studio 2015 to create and open a database file directly without the need to connect to a separate server.
SQL Server Express with LocalDB supports the same SQL syntax as the full Microsoft SQL Server, so it is an appropriate version for beginners to learn on. Download SQL Server express from this link:
http://www.microsoft.com/en-us/server-cloud/products/sql-server-editions/sql-server-express.aspx
The class library in .NET that supports Code First is the newest version of the Entity Framework. The name comes from a database concept called the entity-relationship model, where an entity is the abstract concept of a data object such as a customer, which is related to other entities such as orders and products (for example, a customer places an order for products) in a relational database.
The Entity Framework maps the C# objects in your program to the entities in a relational database. This is called object-relational mapping. Object-relational mapping is code that maps your classes, objects, and properties in C# to the tables, rows, and columns that make up a relational database. Creating this mapping code by hand is tedious and time-consuming, but the Entity Framework makes it easy!
The Entity Framework is built on top of ADO.NET, the low-level data access library built into .NET. ADO.NET requires some knowledge of SQL, but luckily the Entity Framework also handles this for you and lets you concentrate on your C# code.
Also with the Entity Framework you get LINQ to Entities, the LINQ provider for the Entity Framework that makes querying the database in C# easy. Now you'll get started by creating some objects in a database.
In the following Try It Out, you create some objects in a database using Code First with the Entity Framework, then query the objects you created using LINQ to Entities.
But wait, you say. Where is the database you created? You never specified a file name or a folder location—it was all magic! You can see the database in Visual Studio 2015 through the Server Explorer. Go to Tools Connect to Database. The Entity Framework will create a database in the first local SQL Server instance it finds on your computer.
If you never had any databases on your computer previously, Visual C# 2015 creates a local SQL Server instance for you called (localdb)MSSQLLocalDB
. To connect to this database type (localdb)MSSQLLocalDB
into the Server Name field as shown in Figure 21.8.
If you had installed a previous version of Visual Studio before using Visual C# 2015, you might have to enter (localdb)v11.0
into the Server Name field, as this was the previous edition's local database name. Or if you have installed the SQL Server Express Edition, you might have to enter.sqlexpress
, as Entity Framework uses the first local SQL Server database it finds.
The database containing your data will be called BegVCSharp_21_1_CodeFirstDatabase.BookContext
assuming you typed in the example name exactly as shown in the chapter. It will show up in the Select or enter a database name field after taking a moment to connect.
Now you can press OK and the database will appear in the Server Explorer Data Connections window in Visual C# 2015 as shown in Figure 21.9.
From here you can explore the database directly. For example you can right-click on the Books table and choose Show Table Data to see the data you entered as shown in Figure 21.10.
One of the most powerful aspects of the Entity Framework is its capability to automatically create LINQ objects to help you navigate relationships between related tables in the database.
In the following Try It Out, you add two new classes related to the Book class to make a simple bookstore inventory report. The new classes are called Store (to represent each bookstore) and Stock, to represent the inventory of books on hand (in the store on the shelf) and on order from the publisher. A diagram of these new classes and relationships is shown in Figure 21.11.
Each store has a name, address, and an Inventory collection consisting of one or more stock objects, one for each different book (title) carried by the store. The relationship between Store and Stock is one-to-many. Each stock record is related to exactly one book. The relationship between Stock and Book is one-to-one. You need the stock record because one store may have three copies of a particular book, but another store will have six copies of the same book.
You'll see how with Code First, all you have to do is create the C# objects and collections, and the Entity Framework will create the database structure for you and let you easily navigate the relationships between your database objects and then query the related objects in the database.
Follow these steps to create the example in Visual Studio 2015:
C:BegVCSharpChapter21
.Program.cs
. As in the previous example, add the using
statements for the System.Console, System.Data.Entity, and DataAnnotations namespaces, as well as the code to create the Book class:using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using static System.Console;
namespace BegVCSharp_21_2_DatabaseRelations
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
[Key]
public int Code { get; set; }
}
virtual
. You'll see why in the How It Works section. public class Store
{
[Key]
public int StoreId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public virtual List<Stock> Inventory { get; set; }
}
public class Stock
{
[Key]
public int StockId { get; set; }
public int OnHand { get; set; }
public int OnOrder { get; set; }
public virtual Book Item{ get; set; }
}
public class BookContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Store> Stores { get; set; }
public DbSet<Stock> Stocks { get; set; }
}
Main()
method to use the BookContext
and create the two instances of the Book
class as in the previous example: class Program
{
static void Main(string[] args)
{
using (var db = new BookContext())
{
Book book1 = new Book
{
Title = "Beginning Visual C# 2015",
Author = "Perkins, Reid, and Hammer"
};
db.Books.Add(book1);
Book book2 = new Book
{
Title = "Beginning XML",
Author = "Fawcett, Quin, and Ayers"
};
db.Books.Add(book2);
}
using(var db = new BookContext())
clause: var store1 = new Store
{
Name = "Main St Books",
Address = "123 Main St",
Inventory = new List<Stock>()
};
db.Stores.Add(store1);
Stock store1book1 = new Stock
{ Item = book1, OnHand = 4, OnOrder = 6 };
store1.Inventory.Add(store1book1);
Stock store1book2 = new Stock
{ Item = book2, OnHand = 1, OnOrder = 9 };
store1.Inventory.Add(store1book2);
var store2 = new Store
{
Name = "Campus Books",
Address = "321 College Ave",
Inventory = new List<Stock>()
};
db.Stores.Add(store2);
Stock store2book1 = new Stock
{ Item = book1, OnHand = 7, OnOrder = 23 };
store2.Inventory.Add(store2book1);
Stock store2book2 = new Stock
{ Item = book2, OnHand = 2, OnOrder = 8 };
store2.Inventory.Add(store2book2);
db.SaveChanges();
var query = from store in db.Stores
orderby store.Name
select store;
WriteLine("Bookstore Inventory Report:");
foreach (var store in query)
{
WriteLine($"{store.Name} located at {store.Address}");
foreach (Stock stock in store.Inventory)
{
WriteLine($"- Title: {stock.Item.Title}");
WriteLine($"-- Copies in Store: {stock.OnHand}");
WriteLine($"-- Copies on Order: {stock.OnOrder}");
}
}
WriteLine("Press a key to exit…");
ReadKey();
}
}
}
}
Press any key to finish the program and make the console screen disappear. If you used Ctrl+F5 (Start Without Debugging), you might need to press Enter/Return twice. That finishes the program run. Now look at how it works in detail.
The basics of the Entity Framework, DbContext, and data annotations were covered in the previous example, so here you'll concentrate on what is different.
The Store and Stock classes are similar to the original Book class but you added some new virtual
properties for Inventory
and Item
as shown here:
public class Store
{
[Key]
public int StoreId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public virtual List<Stock> Inventory { get; set; }
}
public class Stock
{
[Key]
public int StockId { get; set; }
public int OnHand { get; set; }
public int OnOrder { get; set; }
public virtual Book Item{ get; set; }
}
The Inventory
property looks and behaves like a normal in-memory List<Stock>
collection. However because it is declared as virtual
, the Entity Framework can override its behavior when storing to and retrieving from the database.
The Entity Framework takes care of the database details such as adding a foreign key column to the Stocks table in the database to implement the Inventory
relationship between a Store and its Stock records. Similarly the Entity Framework adds another foreign key column to the Stock table in the database to implement the Item
relationship between Stock and Book. If you're curious you can see this in Server Explorer database design view of the BegVCSharp_21_2_DatabaseRelations.BookContext database as shown in Figure 21.13.
In the past you would have had to decide how to map the collection in your program to foreign keys and columns in the database and keep that code up-to-date as your design changes. However, with the Entity Framework you do not need to know these details; with Code First you simply work with C# classes and collections and let the framework take care of the plumbing for you.
Next you added the DbSet classes for Store and Stock to the BookContext.
public class BookContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Store> Stores { get; set; }
public DbSet<Stock> Stocks { get; set; }
}
Then you used those DbSet classes to create instances of two books, two stores, and two stock records for each book under each store:
class Program
{
static void Main(string[] args)
{
using (var db = new BookContext())
{
Book book1 = new Book
{
Title = "Beginning Visual C# 2015",
Author = "Perkins, Reid, and Hammer"
};
db.Books.Add(book1);
Book book2 = new Book
{
Title = "Beginning XML",
Author = "Fawcett, Quin, and Ayers"
};
db.Books.Add(book2);
var store1 = new Store
{
Name = "Main St Books",
Address = "123 Main St",
Inventory = new List<Stock>()
};
db.Stores.Add(store1);
Stock store1book1 = new Stock
{ Item = book1, OnHand = 4, OnOrder = 6 };
store1.Inventory.Add(store1book1);
Stock store1book2 = new Stock
{ Item = book2, OnHand = 1, OnOrder = 9 };
store1.Inventory.Add(store1book2);
var store2 = new Store
{
Name = "Campus Books",
Address = "321 College Ave",
Inventory = new List<Stock>()
};
db.Stores.Add(store2);
Stock store2book1 = new Stock
{ Item = book1, OnHand = 7, OnOrder = 23 };
store2.Inventory.Add(store2book1);
Stock store2book2 = new Stock
{ Item = book2, OnHand = 2, OnOrder = 8 };
store2.Inventory.Add(store2book2);
After creating the objects, you saved the changes to the database:
db.SaveChanges();
Then you made a simple LINQ query to list all the stores' information:
var query = from store in db.Stores
orderby store.Name
select store;
The code to print out the results of the query is very straightforward because it simply deals with objects and collections, no database-specific code:
WriteLine("Bookstore Inventory Report:");
foreach (var store in query)
{
WriteLine($"{store.Name} located at {store.Address}");
foreach (Stock stock in store.Inventory)
{
WriteLine($"- Title: {stock.Item.Title}");
WriteLine($"-- Copies in Store: {stock.OnHand}");
WriteLine($"-- Copies on Order: {stock.OnOrder}");
}
}
To print the inventory under each store, you simply use a foreach loop like with any collection.
Inevitably as you develop your code, you are going to change your mind. You will come up with a better name for one of your properties, or you will realize you need a new class or relationship. If you change the code in a class connected to a database, via the Entity Framework, you will encounter the Invalid Operation Exception shown in Figure 21.14 when you first run the changed program.
Keeping the database up to date with your changed classes is complicated, but again the Entity Framework steps in with a facility to make it relatively easy. As the error message suggests, you need to add the Code First Migrations package to your program.
To do this, go to Tools NuGet Package Manager Package Manager Console. This brings up a command window as shown in Figure 21.15.
To enable automatic migration of your database to your updated class structure, enter this command in the Package Manager Console at the PM>
prompt:
Enable-Migrations –EnableAutomaticMigrations
This adds a Migrations class to your project, shown in Figure 21.16.
The Entity Framework will compare the timestamp of the database to your program and advise you when the database is out of sync with your classes. To update the database, simply enter this command in the Package Manager Console at the PM>
prompt:
Update-Database
For the last example you will combine all you have learned about LINQ, databases, and XML.
XML is often used to communicate data between client and server machines or between “tiers” in a multitier application. It is quite common to query for some data in a database and then produce an XML document or fragment from that data to pass to another tier.
In the following Try It Out, you create a query to find some data in the previous example database, use LINQ to Entities to query the data, and then use LINQ to XML classes to convert the data to XML. This is an example of Database First as opposed to Code First programming where you take an existing database and generate C# objects from it.
Follow these steps to create the example in Visual Studio 2015:
C:BegVCSharpChapter21
.Program.cs
.System.Xml.Linq
namespace to the beginning of Program.cs
, as shown:using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using static System.Console;
Main()
method in Program.cs
: static void Main(string[] args)
{
using (var db = new BookContext())
{
var query = from store in db.Stores
orderby store.Name
select store;
foreach (var s in query)
{
XElement storeElement = new XElement("store",
new XAttribute("name", s.Name),
new XAttribute("address", s.Address),
from stock in s.Stocks
select new XElement("stock",
new XAttribute("StockID", stock.StockId),
new XAttribute("onHand",
stock.OnHand),
new XAttribute("onOrder",
stock.OnOrder),
new XElement("book",
new XAttribute("title",
stock.Book.Title),
new XAttribute("author",
stock.Book.Author)
)// end book
) // end stock
); // end store
WriteLine(storeElement);
}
Write("Program finished, press Enter/Return to continue:");
ReadLine();
}
}
Simply press Enter/Return to exit the program and make the console screen disappear. If you used Ctrl+F5 (Start Without Debugging), you might need to press Enter/Return twice.
In Program.cs
you added the reference to the System.Xml.Linq
namespace in order to call the LINQ to XML constructor classes in addition to the Entity Framework classes.
When you added the Database First code by choosing ADO.NET Entity Data Model in the Add New Item dialog, Visual Studio generated a separate BookContext.cs class and added it to your project using the information from the existing BegVCSharp_21_2_DatabaseRelations.BookContext database created in the previous example.
In the main program, you created an instance of the BooksContext database context class and the same LINQ to Entities query used in previous examples:
using (var db = new BookContext())
{
var query = from store in db.Stores
orderby store.Name
select store;
When you processed the results of the query in a foreach
loop, you used the LINQ to XML classes to transform the query results into XML using a nested set of LINQ to XML elements and attributes:
foreach (var s in query)
{
XElement storeElement = new XElement("store",
new XAttribute("name", s.Name),
new XAttribute("address", s.Address),
from stock in s.Stocks
select new XElement("stock",
new XAttribute("StockID", stock.StockId),
new XAttribute("onHand",
stock.OnHand),
new XAttribute("onOrder",
stock.OnOrder),
new XElement("book",
new XAttribute("title",
stock.Book.Title),
new XAttribute("author",
stock.Book.Author)
)// end book
) // end stock
); // end store
WriteLine(storeElement);
}
Congratulations! You have combined your data access knowledge from Chapters 19, 20, and 21 into a single program using the full power of LINQ and the Entity Framework!
21.1 Modify the first example BegVCSharp_21_1_CodeFirstDatabase to prompt the user for title and author and store the user-entered data into the database.
21.2 The first example BegVCSharp_21_1_CodeFirstDatabase will create duplicate records if run repeatedly. Modify the example to not create duplicates.
21.3 The generated BookContext class used in the last example BegVCSharp_21_3_XMLfromDatabase does not use the same relationship names as the previous example BegVCSharp_21_2_DatabaseRelations. Modify the BookContext class to use the same relationship names.
21.4 Create a database using Code First to store the data found in the GhostStories.xml
file used in Chapter 19.
Answers to the exercises can be found in Appendix A.
Topic | Key Concepts |
Using Databases | A database is a persistent, structured storehouse for data. While there are many different kinds of databases, the most common type used for business data are relational databases. |
Entity Framework | The Entity Framework is a set of .NET classes for object-relational mapping between C# objects and relational databases. |
How to Create Data with Code First | By using the Code First classes in the Entity Framework you can create databases directly from C# classes and collections using object-relational mapping. |
How to use LINQ with Databases | LINQ to Entities enables powerful queries on databases using the same Entity Framework classes to create the data. |
How to Navigate Database Relationships | The Entity Framework enables creation and navigation of related entities in your database through the use of virtual properties and collections in your C# code. |
How to create and query XML from Databases | You can construct XML from databases by combining LINQ to Entities, LINQ to Objects, and LINQ to XML in a single query. |