"Never put off until run time what can be done at compile time."
David Gries, Compiler Construction for Digital Computers
Introduction
As programmers, when we are learning some new technologies, examples can sometimes be our biggest enemy. Guidelines are often designed to be simple and easy to understand, but at the same time they can lead to an increase in lazy, inefficient and even dangerous code writing. The most common situation like this is in the ADO.NET paradigm. In this article, we'll take a look at what it means to have strongly typed objects in a database, allowing you to do so in your programs, despite the lack of examples.
A little more specifically, we'll see how strongly typed datasets are created and used in Visual Studio 2005. As this article explores, strongly typed data sets offer many advantages over another weakly typed data access technique. We'll also see here that creating and using strongly typed datasets doesn't get any easier with Visual Studio 2005. If you want to learn more, keep reading.
The Basics and Benefits of Strongly-Typed Objects
To understand what strong typing means, you can first think about dating. If you were single, what type of person would you consider dating? You may have specific criteria (such as being healthy and attractive), or the criteria may be simple or unclear. No matter what your conditions are, when you decide who to spend more time with, you will always use your own certain standards for these types to weigh and consider. If you are smart, you will think a lot to protect yourself from emotional trauma. You may find that, for example, being with an alcoholic is unstable unless the two are in a serious relationship. However, it is painful and very difficult to make a person change. Therefore, your wisdom will instruct you to stop the relationship before it even begins. Adding a no-drinking clause to your dating criteria will protect you from future heartache and allow you to focus your time and energy on better candidates.
You may be surprised how this reasoning has anything to do with programming. It doesn’t matter, come with me, lovely reader! ADO.NET data access objects are designed to be extremely flexible. When you read data from a database, you're probably working with many of the common types of objects allowed by the normal .NET framework, unless you encounter special problems. Applying our dating theory, you can basically think of your relevant data as universal objects. "As long as my date isn't too much trouble." Can't you be more clear? There is no limit even if it is a human or other living creature! As your friend, I implore you, "More standards! Make your list smaller!"
Just as neglecting who you date can lead to relationship problems in the future, leaving your objects unchecked in your code can also cause errors. Also, if you let old objects wander around in your subroutine, you may not notice that this is a problem until the program is running. To use our dating theory, catching errors at runtime is like having your date have a painful and awkward argument in the middle of a trendy Italian restaurant. Yes, you see, if you had planned ahead, you wouldn't have ended up with a bunch of diners looking at you, and it wouldn't have been embarrassing. By simply applying some stricter standards to your code, you can catch errors before your program starts compiling. For example, the following code example:
string FirstName = myrow.("FirstName").ToString();
The DataRow in this example is untyped, and as a result, you must use the column name as a string to get the value you need (or you can choose to use the index of the column in the record's column collection). Fortunately that column does exist. The data type of the DataRow column is object. We assume that the data type under the FirstName column is string, and we must explicitly convert it to string before using it. If the name of this column changes (for example, becomes PersonFirstName), the compiler has no way to notify you. Depressed? But you don't have to. If your code looks like the following, your life will be simpler and your code will be more reliable.
string FirstName = PersonRow.FirstName;
In this second example, we use a strongly typed row, and we know that the FirstName property is of type string. No messy column names, no messy type conversions. The compiler has already done the type checking for us, and we can safely carry out other work without worrying about whether we have typed the column names correctly.
Everything else is the same, so you won't hesitate to use this instead of the generic type. But wait a minute, where do strongly typed objects come from? I also wish I could tell you that these objects are created automatically. But just as good relationships take time and effort, making your objects strongly typed takes extra effort. But the extra time spent here is definitely worth it, and it saves exponentially more time spent "catching bugs" in the future.
There are several ways to accomplish strong typing, and we'll spend the rest of this article explaining how to create a strongly typed dataset in Visual Studio 2005. We will also compare the advantages and disadvantages of this approach with other approaches.
Creating Strongly-Typed DataSets in Visual Studio 2005
Strongly typed data sets are actually just predefined columns and tables of ordinary data sets, so the compiler already knows what they contain. Instead of a loose wrapper that fits you like a baseball glove, strongly typed datasets fit like a glove. Each successive version of Visual Studio makes it easier to handle strongly typed data sets. In the following example, we will use the AdventureWorks database of SQL Server 2005. Simply follow these steps:
1. Open Visual Studio and create a new ASP.NET website.
2. In the Solution Explorer window, right-click to add a new item and select DataSet. Name it AdventureWorks.xsd (see screenshot). Visual Studio will recommend that you put the DataSet file into the App_Code file, and you just have to click Agree.
3. After opening AdventureWorks.xsd in design mode, the TableAdapter configuration wizard will run. At this point, click Cancel and we will drag in the desired table from Server Explorer.
4. Browse to find the AdventureWorks database in the Server Explorer toolbar. (If you have not installed the AdventureWorks database, you can go to Microsoft's download page SQL Server 2005 Samples and Sample Databases to download it and some other SQL Server 2005 samples)
5. Drag the SalesOrderHeader table and SalesOrderDetail table into the DataSet design window. The window should look like in the screenshot. What are we seeing? Whenever we add a table, Visual Studio will create a strongly typed DataTable (with the same name as the original table) and a TableAdapter. This DataTable has defined each column for us. TableAdapter is what we use to fill the table. By default, there is a Fill() method to get each row of data from the original table.
As it is, this strongly typed dataset will return all records from both tables. But the AdventureWorks database contains a lot of order information, so why not create a more explicit query? We can add methods to the TableAdapter object to obtain a specific sub-record set. Right-click SalesORderHeaderTableAdapter and select Add|Query. Select "Use SQL statements" and click Next, then select "SELECT which returns rows" and click Next. Recently, enter the following query into the window (or you can use Query Builder to do the job):
SELECT
SalesOrderID, RevisionNumber, OrderDate, DueDate, ShipDate,
Status, OnlineOrderFlag, SalesOrderNumber, PurchaseOrderNumber,
AccountNumber, CustomerID, ContactID, SalesPersonID, TerritoryID,
BillToAddressID, ShipToAddressID, ShipMethodID, CreditCardID,
CreditCardApprovalCode, CurrencyRateID, SubTotal, TaxAmt, Freight,
TotalDue, Comment, rowguid, ModifiedDate
FROM Sales.SalesOrderHeader
WHERE (OrderDate > @OrderDate)
This SQL query is a simple SELECT query, using an @OrderDate parameter to filter the results. This will save us from returning all records in the database. Keep the "Fill a DataTable" and "Return a DataTable" checkboxes selected and click Finish. After adding this SELECT statement, your designer should now look like the screenshot, with an additional query under SalesOrderHeaderTableAdapter.
After the strongly typed data set is established, we can easily display the data in the ASP.NET page with a few lines of code. Create a new ASP.NET page in the website and switch to design mode. Drag a GridView control onto it and leave its ID as GirdView1. Then go to the source code page and introduce the AdventureWorksTableAdapters namespace above the file (the syntax in c# is using AdventureWorksTableAdapters;). Finally, add the following code to the Page_Load event:
// Create the SalesOrderHeaderTableAdapter
SalesOrderHeaderTableAdapter salesAdapter =
new SalesOrderHeaderTableAdapter();
// Get orders that took place after July 1st, 2004
AdventureWorks.SalesOrderHeaderDataTable Orders =
salesAdapter.GetDataBy(new DateTime(2004, 7, 1));
// Bind the order results to the GridView
this.GridView1.DataSource = Orders;
this.GridView1.DataBind();
The code is very simple. We create an instance of SalesORderHeaderTableAdapter to populate the data table. What should be noted here is that unlike an ordinary DataTable, we declare an object of type SalesORderHeaderDataTable. We call the GetDateBy() method and pass a DateTime object to fill in the data. Also note here that the command obtained is also strongly typed, so we must pass a DateTime object instead of an ordinary object. The screenshot below is the result of the code example above.
In addition to using code to bind the result set to the GridView, you can also use an ObjectDataSource, set its TypeName property to AdventureWorksTableAdapters.SalesOrderHeaderTableAdapter, and set its SelectMethod to GetData or GetDataBy.
In addition to not having to write code to connect to the database, another big advantage of using a strongly typed dataset is that there are no column name strings lurking in our code that the compiler can't check. We don't need to do any type conversion either. If the database schema changes, just update the AdventureWorks.xsd file and we will find that all related changes are automatically completed at compile time.
Other Techniques for Generating Strongly-Typed Data-Access Applications
In addition to using strongly typed data sets, there are other ways to implement strong typing in your programs. You can create custom classes that are more lightweight than DataSets and more consistent with your database. There are also some third-party software developers who have developed tools to automate this process. One of the more special ones and my favorite is LLBLGen Pro. I once wrote a book about it: Rapid C# Windows Development: Visual Studio 2005, SQL Server 2005, and LLBLGen Pro. (You can read 1/3 of the book for free on my website.) Another popular tool is CodeSmith. Even Microsoft is developing a small tool called DLINQ, but it is still being tested and it is estimated that it will not be launched until at least next year.
If you use Visual Studio's strong data set approach, one of the undeniable advantages is that you don't need to purchase additional software. All of these solutions have different features and benefits, but the main benefits are reliability, fewer errors, and less time spent debugging. It is also easier to check the impact of database schema changes and perform maintenance. Hopefully you've realized the benefits of strong typing. Good luck with development (and dating too)!
By Joseph Chancellor
Attachments
Download the code examined in this article
About the Author
Joseph Chancellor is a C# developer in Southern California who has had his fair share of relational trauma. He appreciates all kinds of feedback and suggestions. Visit his blog or read the first five chapters of his book on Visual Studio 2005, SQL Server 2005, and LLBLGen Pro.
Original address: http://aspnet.4guysfromrolla.com/articles/020806-1.aspx