
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
822 CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER
Figure 23-17. Selecting the type of data source
■Note This step of the wizard also allows you to connect data that comes from an external XML web service or a custom business object within a separate .NET assembly.
The second step (which will differ slightly based on your selection in step 1) allows you to configure your database connection. If you have a database currently added to Server Explorer, you should find it automatically listed in the drop-down list. If this is not the case (or if you ever need to connect to a database you have not previously added to Server Explorer), click the New Connection button. Figure 23-18 shows the result of selecting the local instance of AutoLot.
The third step asks you to confirm that you wish to save your connection string within an external App.config file, and if so, the name to use within the <connectionStrings> element. Keep the default settings for this step of the wizard and click the Next button.
The final step of the wizard is where you are able to select the database objects that will be accounted for by the autogenerated DataSet and related data adapters. While you could select each of the data objects of the AutoLot database, here you will only concern yourself with the Inventory table. Given this, change the suggested name of the DataSet to InventoryDataSet (see Figure 23-19), check the Inventory table, and click the Finish button.



CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER |
825 |
The App.config File and the Settings.Settings File
If you examine your Solution Explorer, you will find your project now contains an App.config file. If you open this file, you will notice the name attribute of the <connectionStrings> element used in previous examples:
<?xml version="1.0" encoding="utf-8" ?> <configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="VisualDataGridViewApp.Properties.Settings.AutoLotConnectionString" connectionString=
"Data Source=(local)\SQLEXPRESS;
Initial Catalog=AutoLot;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Specifically, the lengthy "VisualDataGridViewApp.Properties.Settings. AutoLotConnectionString" value has been set as the name of the connection string. Even stranger is the fact that if you scan all of the generated code, you will not find any reference to the ConfigurationManager type to read the value from the <connectionStrings> element. However, you will find that the autogenerated data adapter object (which you will examine in more detail in just a moment) is constructed in part by calling the following private helper function:
private void InitConnection()
{
this._connection = new global::System.Data.SqlClient.SqlConnection(); this._connection.ConnectionString = global::
VisualDataGridViewApp.Properties.Settings.Default.AutoLotConnectionString;
}
As you can see, the ConnectionString property is set via a call to Settings.Default. As it turns out, every Visual Studio 2008 project type maintains a set of application-wide settings that are burned into your assembly as metadata when you compile the application. The short answer is that if you open your compiled application using reflector.exe (see Chapter 2), you can view this internal type (see Figure 23-22).
Given the previous point, it would be possible to deploy your application without shipping the *.config file, as the embedded value will be used by default if a client-side *.config file is not present.
■Note The Visual Studio 2008 settings programming model is really quite interesting; however, full coverage is outside of the scope of this chapter (and this edition of the text, for that matter). If you are interested in learning more, look up the topic “Managing Application Settings” in the .NET Framework 3.5 SDK documentation.


CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER |
827 |
If you double-click the InventoryDataSet.xsd file within Solution Explorer, you will load the Visual Studio 2008 Dataset Designer (more details on this designer in just a bit). If you right-click anywhere within this designer and select the View Code option, you will notice a fairly empty partial class definition:
public partial class InventoryDataSet { partial class InventoryDataTable
{
}
}
The real action is taking place within the designer-maintained file, InventoryDataSet. Designer.cs. If you open this file using Solution Explorer, you will notice that InventoryDataSet is actually extending the DataSet class type. When you (or a wizard) create a class extending DataSet, you are building what is termed a strongly typed DataSet. One benefit of using strongly typed DataSet objects is that they contain a number of properties that map directly to the database tables names. Thus, rather than having to drill into the collection of tables using the Tables property, you can simply use the Inventory property. Consider the following partial code, commented for clarity:
// This is all designer-generated code!
public partial class InventoryDataSet : global::System.Data.DataSet
{
//A member variable of type InventoryDataTable. private InventoryDataTable tableInventory;
//Each constructor calls a helper method named InitClass(). public InventoryDataSet()
{
...
this.InitClass();
}
//InitClass() preps the DataSet and adds the InventoryDataTable
//to the Tables collection.
private void InitClass()
{
this.DataSetName = "InventoryDataSet"; this.Prefix = "";
this.Namespace = "http://tempuri.org/InventoryDataSet.xsd"; this.EnforceConstraints = true; this.SchemaSerializationMode =
global::System.Data.SchemaSerializationMode.IncludeSchema; this.tableInventory = new InventoryDataTable(); base.Tables.Add(this.tableInventory);
}
//The read-only Inventory property returns
//the InventoryDataTable member variable. public InventoryDataTable Inventory
{
get { return this.tableInventory; }
}
}
In addition to wrapping the details of maintaining a DataTable object, the designer-generated strongly typed DataSet could contain similar logic to expose any DataRelation objects (which we do not currently have) that represent the connections between each of the tables.



830 CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER
The autogenerated InventoryTableAdapter type maintains a collection of SqlCommand objects, each of which has a fully populated set of SqlParameter objects (this alone is a massive time-saver). Furthermore, this custom data adapter provides a set of properties to extract the underlying connection, transaction, and data adapter objects, as well as a property to obtain an array representing each command type. The obvious benefit is you did not have to author the code!
Using the Generated Types in Code
If you were to examine the Load event handler of the form-derived type, you will find that the Fill() method of the custom data adapter is called upon startup, passing in the custom DataTable maintained by the custom DataSet:
private void MainForm_Load(object sender, EventArgs e)
{
this.inventoryTableAdapter.Fill(this.inventoryDataSet.Inventory);
}
You can use this same custom data adapter object to update changes to the grid. Update the UI of your form with a single Button control (named btnUpdateInventory). Handle the Click event, and author the following code within the event handler:
private void btnUpdateInventory_Click(object sender, EventArgs e)
{
//This will push any changes within the Inventory table back to
//the database for processing. this.inventoryTableAdapter.Update(this.inventoryDataSet.Inventory);
//Get fresh copy for grid. this.inventoryTableAdapter.Fill(this.inventoryDataSet.Inventory);
}
Run your application once again; add, delete, or update the records displayed in the grid; and click the Update button. When you run the program again, you will find your changes are present and accounted for.
Understand that you are able to make use of each of these strongly typed classes directly in your code, in (more or less) the same way you have been doing throughout this chapter. For example, assume you have updated your form with a new chunk of UI real estate (see Figure 23-27) that allows the user to enter a new record using a series of text boxes (granted, this is a bit redundant for this example, as the DataGridView will do so on your behalf).
Within the Click event handler of the new Button, you could author the following code:
private void btnAddRow_Click(object sender, EventArgs e)
{
// Get data from widgets
int id = int.Parse(txtCarID.Text); string make = txtMake.Text; string color = txtColor.Text; string petName = txtPetName.Text;
//Use custom adapter to add row. inventoryTableAdapter.Insert(id, make, color, petName);
//Refill table data. this.inventoryTableAdapter.Fill(this.inventoryDataSet.Inventory);
}

CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER |
831 |
Figure 23-27. A simple update to the form type
Or, if you so choose, you can manually add a new row:
private void btnAddRow_Click(object sender, EventArgs e)
{
//Get new Row.
InventoryDataSet.InventoryRow newRow = inventoryDataSet.Inventory.NewInventoryRow();
newRow.CarID = int.Parse(txtCarID.Text); newRow.Make = txtMake.Text; newRow.Color = txtColor.Text; newRow.PetName = txtPetName.Text;
inventoryDataSet.Inventory.AddInventoryRow(newRow);
//Use custom adapter to add row. inventoryTableAdapter.Update(inventoryDataSet.Inventory);
//Refill table data. this.inventoryTableAdapter.Fill(this.inventoryDataSet.Inventory);
}
■Source Code The VisualDataGridViewApp project is included under the Chapter 23 subdirectory.
Decoupling Autogenerated Code from the UI Layer
To close, allow me to point out that while the Data Source Configuration Wizard launched by the DataGridView has done a fantastic job of authoring a ton of grungy code on our behalf, the previous example hard-coded the data access logic directly within the user interface layer—a major design faux pas. Ideally, this sort of code belongs in our AutoLotDAL.dll assembly (or some other data