Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 140 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
140
Dung lượng
5,73 MB
Nội dung
Configuring the Data Adapter Using the SqlCommandBuilder When you are using a data adapter to modify tables in a DataSet, the first order of business is to assign the UpdateCommand, DeleteCommand, and InsertCommand properties with valid command objects (until you do so, these properties return null references). By “valid” command objects, I am referring to the set of command objects used in conjunction with the table you are attempting to update (the Inventory table in our example). To fill up our adapter with the necessary data can entail a good amount of code, especially if we make use of parameterized queries. Recall from Chapter 22 that a parameterized query allows us to build a SQL statement using a set of parameter objects. Thus, if we were to take the long road, we could implement ConfigureAdapter() to manually create three new SqlCommand objects, each of which contains a set of SqlParameter objects. After this point, we could set each object to the UpdateCommand, DeleteCommand, and InsertCommand properties of the adapter. Thankfully, Visual Studio 2008 provides a number of designer tools to take care of this mun- dane and tedious code on our behalf. You’ll see some of these shortcuts in action at the conclusion of this chapter. Rather than forcing you to author the numerous code statements to fully configure a data adapter, let’s take a massive shortcut by implementing ConfigureAdapter() as so: private void ConfigureAdapter(out SqlDataAdapter dAdapt) { // Create the adapter and set up the SelectCommand. dAdapt = new SqlDataAdapter("Select * From Inventory", cnString); // Obtain the remaining command objects dynamically at runtime // using the SqlCommandBuilder. SqlCommandBuilder builder = new SqlCommandBuilder(dAdapt); } To help simplify the construction of data adapter objects, each of the Microsoft-supplied ADO.NET data providers provides a command builder type. The SqlCommandBuilder automatically generates the values contained within the SqlDataAdapter’s InsertCommand, UpdateCommand, and DeleteCommand properties based on the initial SelectCommand. Clearly, the benefit is that you have no need to build all the SqlCommand and SqlParameter types by hand. An obvious question at this point is how a command builder is able to build these SQL com- mand objects on the fly. The short answer is metadata. At runtime, when you call the Update() method of a data adapter, the related command builder will read the database’s schema data to autogenerate the underlying insert, delete, and update command objects. Obviously, doing so requires additional round-trips to the remote database, and therefore it will certainly hurt performance if you use the SqlCommandBuilder numerous times in a single appli- cation. Here, we are minimizing the negative effect by calling our ConfigureAdapter() method at the time the InventoryDALDisLayer object is constructed, and retaining the configured SqlDataAdapter for use throughout the object’s lifetime. In the previous code, notice that we made no use of the command builder object ( SqlCommandBuilder in this case) beyond passing in the data adapter object as a constructor parameter. As odd as this may seem, this is all we are required to do (at a minimum). Under the hood, this type will configure the data adapter with the remaining command objects. Now, while you may love the idea of getting something for nothing, do understand that com- mand builders come with some critical restrictions. Specifically, a command builder is only able to autogenerate SQL commands for use by a data adapter if all of the following conditions are true: • The SQL Select command interacts with only a single table (e.g., no joins). • The single table has been attributed with a primary key. • The table must have a column(s) representing the primary key that is included in your SQL Select statement. CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER812 8849CH23.qxd 10/22/07 1:54 PM Page 812 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Based on the way we constructed our AutoLot database, these restrictions pose no problem. However, in a more industrial-strength database, you will need to consider if this type is at all useful (if not, remember that Visual Studio 2008 will autogenerate a good deal of the required code, as you’ll see at the end of this chapter). Implementing GetAllInventory() Now that our data adapter is ready to go, the first method of our new class type will simply use the Fill() method of the SqlDataAdapter object to fetch a DataTable representing all records in the Inventory table of the AutoLot database: public DataTable GetAllInventory() { DataTable inv = new DataTable("Inventory"); dAdapt.Fill(inv); return inv; } Implementing UpdateInventory() The UpdateInventory() method is very simple: public void UpdateInventory(DataTable modifiedTable) { dAdapt.Update(modifiedTable); } Here, the data adapter object will examine the RowState value of each row of the incoming DataTable. Based on this value (RowState.Added, RowState.Deleted, or RowState.Modified), the cor- rect command object will be leveraged behind the scenes. ■Source Code The AutoLotDAL (Part 2) project is included under the Chapter 23 subdirectory. Building a Windows Forms Front End At this point we can build a front end to test our new InventoryDALDisLayer object, which will be a Windows Forms application named WindowsFormsInventoryUI. Once you have created the proj- ect, set a reference to your updated AutoLotDAL.dll assembly and import the following namespace: using AutoLotDisconnectedLayer; The design of the form consists of a single Label, DataGridView (named inventoryGrid), and Button type (named btnUpdateInventory), which has been configured to handle the Click event. Here is the definition of the form (which does not contain error-handling logic for simplicity; feel free to add try/catch logic if you so choose): public partial class MainForm : Form { InventoryDALDisLayer dal = null; public MainForm() { InitializeComponent(); CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER 813 8849CH23.qxd 10/22/07 1:54 PM Page 813 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Assume we have an App.config file // storing the connection string. string cnStr = ConfigurationManager.ConnectionStrings["AutoLotSqlProvider"].ConnectionString; // Create our data access object. dal = new InventoryDALDisLayer(cnStr); // Fill up our grid! inventoryGrid.DataSource = dal.GetAllInventory(); } private void btnUpdateInventory_Click(object sender, EventArgs e) { // Get modified data from the grid. DataTable changedDT = (DataTable)inventoryGrid.DataSource; // Commit our changes. dal.UpdateInventory(changedDT); } } Notice that in this example, I am assuming you have added an App.config file to store the con- nection string data, within a <connectionStrings> section. To make use of the ConnectionStrings indexer of the ConfigurationManager type, be sure to set a reference to the System.Configuration.dll assembly. Once we create the InventoryDALDisLayer object, we bind the DataTable returned from GetAllInventory() to the DataGridView object. When the end user clicks the Update button, we extract out the modified DataTable from the grid (via the DataSource property) and pass it into our UpdateInventory() method. That’s it! Once you run this application, add a set of new rows to the grid and update/delete a few others. Assuming you click the Button control, you will see your changes have persisted into the AutoLot database. ■Source Code The updated WindowsFormsInventoryUI project is included under the Chapter 23 subdirectory. Navigating Multitabled DataSet Objects So far, all of this chapter’s examples have operated on a single DataTable object. However, the power of the disconnected layer really comes to light when a DataSet object contains numerous interre- lated DataTables. In this case, you are able to insert any number of DataRelation objects into the DataSet’s DataRelation collection to account for the interdependencies of the tables. Using these objects, the client tier is able to navigate between the table data without incurring network round-trips. ■Note Rather than updating AutoLotDAL.dll yet again in order to account for the Customers and Orders tables, this example isolates all of the data access logic within a new Windows Forms project. However, intermix- ing UI and data logic in a production-level application is certainly not recommended. The final examples of this chapter leverage various database design tools to decouple the UI and data logic code. CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER814 8849CH23.qxd 10/22/07 1:54 PM Page 814 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Begin this example by creating a new Windows Forms application named MultitabledDataSetApp. The GUI is simple enough. In Figure 23-13 you can see three DataGridView widgets that hold the data retrieved from the Inventory, Orders, and Customers tables of the AutoLot database. In addition, the initial Button (named btnUpdateDatabase) submits any and all changes entered within the grids back to the database for processing via data adapter objects. Figure 23-13. The initial UI will display data from each table of the AutoLot database. Prepping the Data Adapters To keep the data access code as simple as possible, the MainForm will make use of command builder objects to autogenerate the SQL commands for each of the three SqlDataAdapters (one for each table). Here is the initial update to the Form-derived type: public partial class MainForm : Form { // Form wide DataSet. private DataSet autoLotDS = new DataSet("AutoLot"); // Make use of command builders to simplify data adapter configuration. private SqlCommandBuilder sqlCBInventory; private SqlCommandBuilder sqlCBCustomers; private SqlCommandBuilder sqlCBOrders; // Our data adapters (for each table). private SqlDataAdapter invTableAdapter; private SqlDataAdapter custTableAdapter; private SqlDataAdapter ordersTableAdapter; CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER 815 8849CH23.qxd 10/22/07 1:54 PM Page 815 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Form wide connection string. private string cnStr = string.Empty; } The constructor does the grunge work of creating your data-centric member variables and fill- ing the DataSet. Here, I am assuming you have authored an App.config file that contains the correct connection string data (and that you have referenced System.Configuration.dll and imported the System.Configuration namespace). Also note that there is a call to a private helper function, BuildTableRelationship(), as shown here: public MainForm() { InitializeComponent(); // Get connection string from *.config file. cnStr = ConfigurationManager.ConnectionStrings["AutoLotSqlProvider"].ConnectionString; // Create adapters. invTableAdapter = new SqlDataAdapter("Select * from Inventory", cnStr); custTableAdapter = new SqlDataAdapter("Select * from Customers", cnStr); ordersTableAdapter = new SqlDataAdapter("Select * from Orders", cnStr); // Autogenerate commands. sqlCBInventory = new SqlCommandBuilder(invTableAdapter); sqlCBOrders = new SqlCommandBuilder(ordersTableAdapter); sqlCBCustomers = new SqlCommandBuilder(custTableAdapter); // Add tables to DS. invTableAdapter.Fill(autoLotDS, "Inventory"); custTableAdapter.Fill(autoLotDS, "Customers"); ordersTableAdapter.Fill(autoLotDS, "Orders"); // Build relations between tables. BuildTableRelationship(); // Bind to grids dataGridViewInventory.DataSource = autoLotDS.Tables["Inventory"]; dataGridViewCustomers.DataSource = autoLotDS.Tables["Customers"]; dataGridViewOrders.DataSource = autoLotDS.Tables["Orders"]; } Building the Table Relationships The BuildTableRelationship() helper function does the grunt work to add two DataRelation objects into the autoLotDS object. Recall from Chapter 22 that the AutoLot database expresses a number of parent/child relationships, accounted for with the following code: private void BuildTableRelationship() { // Create CustomerOrder data relation object. DataRelation dr = new DataRelation("CustomerOrder", autoLotDS.Tables["Customers"].Columns["CustID"], autoLotDS.Tables["Orders"].Columns["CustID"]); autoLotDS.Relations.Add(dr); CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER816 8849CH23.qxd 10/22/07 1:54 PM Page 816 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Create InventoryOrder data relation object. dr = new DataRelation("InventoryOrder", autoLotDS.Tables["Inventory"].Columns["CarID"], autoLotDS.Tables["Orders"].Columns["CarID"]); autoLotDS.Relations.Add(dr); } Note that when creating a DataRelation object, you establish a friendly string moniker with the first parameter (you’ll see the usefulness of doing so in just a minute) as well as the keys used to build the relationship itself. Notice that the parent table (the second constructor parameter) is specified before the child table (the third constructor parameter). Updating the Database Tables Now that the DataSet has been filled and disconnected from the data source, you can manipulate each DataTable locally. To do so, simply insert, update, or delete values from any of the three DataGridViews. When you are ready to submit the data back for processing, click the Update button. The code behind the related Click event should be clear at this point: private void btnUpdateDatabase_Click(object sender, EventArgs e) { try { invTableAdapter.Update(carsDS, "Inventory"); custTableAdapter.Update(carsDS, "Customers"); ordersTableAdapter.Update(carsDS, "Orders"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } Now run your application and perform various updates. When you rerun the application, you should find that your grids are populated with the recent changes. Navigating Between Related Tables To illustrate how a DataRelation allows you to move between related tables programmatically, extend your UI to include a new Button type (named btnGetOrderInfo), a related TextBox (named txtCustID), and a descriptive Label (I grouped these controls within a GroupBox simply for visual appeal). Figure 23-14 shows one possible UI of the application. CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER 817 8849CH23.qxd 10/22/07 1:54 PM Page 817 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 23-14. The updated UI allows the user to look up customer order information. Using this updated UI, the end user is able to enter the ID of a customer and retrieve all the relevant information about that customer’s order (name, order ID, car order, etc.), which will be formatted into a string type that is eventually displayed within a message box. Ponder the code behind the new Button’s Click event handler: private void btnGetOrderInfo_Click(object sender, System.EventArgs e) { string strOrderInfo = string.Empty; DataRow[] drsCust = null; DataRow[] drsOrder = null; // Get the customer ID in the text box. int custID = int.Parse(this.txtCustID.Text); // Now based on custID, get the correct row in Customers table. drsCust = autoLotDS.Tables["Customers"].Select( string.Format("CustID = {0}", custID)); strOrderInfo += string.Format("Customer {0}: {1} {2}\n", drsCust[0]["CustID"].ToString(), drsCust[0]["FirstName"].ToString().Trim(), drsCust[0]["LastName"].ToString().Trim()); // Navigate from Customers table to Orders table. drsOrder = drsCust[0].GetChildRows(autoLotDS.Relations["CustomerOrder"]); // Get order number. foreach (DataRow r in drsOrder) strOrderInfo += string.Format("Order Number: {0}\n", r["OrderID"]); // Now navigate from Orders table to Inventory table. DataRow[] drsInv = drsOrder[0].GetParentRows(autoLotDS.Relations["InventoryOrder"]); // Get car info. foreach (DataRow r in drsInv) { strOrderInfo += string.Format("Make: {0}\n", r["Make"]); CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER818 8849CH23.qxd 10/22/07 1:54 PM Page 818 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com strOrderInfo += string.Format("Color: {0}\n", r["Color"]); strOrderInfo += string.Format("Pet Name: {0}\n", r["PetName"]); } MessageBox.Show(strOrderInfo, "Order Details"); } Let’s break down this code step by step. First, you obtain the correct customer ID from the text box and use it to select the correct row in the Customers table, via the Select() method. Given that Select() returns an array of DataRow objects, you must use double indexing to ensure you fetch the data for the first (and only) member of this array: // Get the customer ID in the text box. int custID = int.Parse(this.txtCustID.Text); // Now based on custID, get the correct row in Customers table. drsCust = autoLotDS.Tables["Customers"].Select( string.Format("CustID = {0}", custID)); strOrderInfo += string.Format("Customer {0}: {1} {2}\n", drsCust[0]["CustID"].ToString(), drsCust[0]["FirstName"].ToString().Trim(), drsCust[0]["LastName"].ToString().Trim()); Next, you navigate from the Customers table to the Orders table, using the CustomerOrder data relation. Notice that the DataRow.GetChildRows() method allows you to grab rows from your child table. Once you do, you can read information out of the table: // Navigate from Customers table to Orders table. drsOrder = drsCust[0].GetChildRows(autoLotDS.Relations["CustomerOrder"]); // Get order number. foreach (DataRow r in drsOrder) strOrderInfo += string.Format("Order Number: {0}\n", r["OrderID"]); The final step is to navigate from the Orders table to its parent table (Inventory), using the GetParentRows() method. At this point, you can read information from the Inventory table using the Make, PetName, and Color columns, as shown here: // Now navigate from Orders table to Inventory table. DataRow[] drsInv = drsOrder[0].GetParentRows(autoLotDS.Relations["InventoryOrder"]); // Get car info. foreach (DataRow r in drsInv) { strOrderInfo += string.Format("Make: {0}\n", r["Make"]); strOrderInfo += string.Format("Color: {0}\n", r["Color"]); strOrderInfo += string.Format("Pet Name: {0}\n", r["PetName"]); } Figure 23-15 shows one possible output when specifying a customer ID with the value of 2 (Matt Walton in my copy of the AutoLot database). CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER 819 8849CH23.qxd 10/22/07 1:54 PM Page 819 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 23-15. Navigating data relations Hopefully, this last example has you convinced of the usefulness of the DataSet type. Given that a DataSet is completely disconnected from the underlying data source, you can work with an in- memory copy of data and navigate around each table to make any necessary updates, deletes, or inserts. Once you’ve finished, you can submit your changes to the data store for processing. The end result is a very scalable and robust application. ■Source Code The MultitabledDataSetApp project is included under the Chapter 23 subdirectory. The Data Access Tools of Visual Studio 2008 All of the ADO.NET examples in this text thus far have involved a fair amount of elbow grease, in that we were authoring all data access logic by hand. While we did offload a good amount of said code to a .NET code library ( AutoLotDAL.dll) for reuse in later chapters of the book, we were still required to manually create the various objects of our data provider before interacting with the relational database. To wrap up our examination of the disconnected layer of ADO.NET, we will now take a look at a number of services provided by Visual Studio 2008 that can assist you in authoring data access logic. As you might suspect, this IDE supports a number of visual designers and code generation tools (aka wizards) that can produce a good deal of starter code. CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER820 8849CH23.qxd 10/22/07 1:54 PM Page 820 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ■Note Don’t get lulled into the belief that you will never be required to author ADO.NET logic by hand, or that the wizard-generated code will always fit the bill 100 percent for your current project. While these tools can save you a significant amount of time, the more you know about the ADO.NET programming model, the better, as this enables you to customize and tweak the generated code as required. Visually Designing the DataGridView The first data access shortcut can be found via the DataGridView designer. While we have used this widget in previous examples for display and editing purposes, we have not used the associated wiz- ard that will generate data access code on our behalf. To begin, create a brand-new Windows Forms application project named VisualDataGridViewApp. Add a descriptive Label control and an instance of the DataGridView control. When you do, note that an inline editor opens to the right of the UI widget. From the Choose Data Source drop-down box, select the Add Project Data Source link (see Figure 23-16). Figure 23-16. The DataGridView editor The Data Source Configuration Wizard launches. This tool will guide you through a series of steps that allow you to select and configure a data source, which will then be bound to the DataGridView using a custom data adapter type. The first step of the wizard simply asks you to identify the type of data source you wish to interact with. Select Database (see Figure 23-17) and click the Next button. CHAPTER 23 ■ ADO.NET PART II: THE DISCONNECTED LAYER 821 8849CH23.qxd 10/22/07 1:54 PM Page 821 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... disconnected from the remote data store You also examined the role of the data adapter type in this chapter Using this type (and the related SelectCommand, InsertCommand, UpdateCommand, and DeleteCommand properties), the adapter can resolve changes in the DataSet with the original data store As well, you learned how to navigate the object model of a DataSet using the brute-force manual approach, as well... chapter Recall that the original example made use of a loosely typed DataSet and a batch of SqlDataAdapter types to move the table data to and fro This updated version makes use of the third iteration of AutoLotDAL.dll and the wizard-generated types While I won’t bother to list all of the code here (as it is more or less the same as the first iteration of this project), here are the highlights: • You... manually create or configure the relationships between your tables, as the Dataset Designer has done so automatically Regarding the last bullet point, be aware that the names the Dataset Designer gave the table relationships are different from the names we gave to them in the first iteration of this project Therefore, the btnGetOrderInfo_Click() method must be updated to use the correct relationship names... 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... dal.GetAllInventory(); o rg While all of these approaches have their place, LINQ to DataSet provides yet another option to manipulate the contained data using LINQ query expressions Out of the box, the ADO.NET DataSet (and related types such as DataTable and DataView) do not have the necessary infrastructure to be a direct target for a LINQ query For example, the following method would result in a compile-time... SQL queries against the database At absolute minimum, you will make use of the [Table] and [Column] attributes; however, additional attributes exist to mark the methods that perform SQL insert, update, and delete commands As well, each of the LINQ to SQL attributes defines a set of properties that further qualify to the LINQ to SQL runtime engine how to process the annotated item The [Table] attribute... class extends DataRow and exposes properties that map directly to the schema of the Inventory table (also be aware that the columns are appropriately typed) 8849CH23.qxd 10/22/ 07 1:55 PM Page 829 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com rg CHAPTER 23 s ADO.NET PART II: THE DISCONNECTED LAYER oa d o Figure 23-25 The custom DataRow type nl Examining the Generated Data Adapter... 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... by default Thankfully, you can activate the data design tools of Visual Studio 2008 from any sort of project (UI based or otherwise) without the need to copy and paste massive amounts of code between projects To illustrate some of your options, open your AutoLotDAL project once again and insert into your project a new DataSet type (named AutoLotDataSet) via the Project ® Add New Item menu option (see... w The Role of the DataSet Extensions w The System.Data.DataSetExtensions.dll assembly extends the System.Data namespace with a handful of new members (see Figure 24-1) Figure 24-1 The System.Data.DataSetExtensions.dll assembly 839 8849CH24.qxd 10/19/ 07 10:11 AM Page 840 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 24 s PROGRAMMING WITH THE LINQ APIS Far and away the . command builder type. The SqlCommandBuilder automatically generates the values contained within the SqlDataAdapter’s InsertCommand, UpdateCommand, and DeleteCommand properties based on the initial. 23- 14 shows one possible UI of the application. CHAPTER 23 ■ ADO .NET PART II: THE DISCONNECTED LAYER 8 17 8849CH 23. qxd 10/22/ 07 1 :54 PM Page 8 17 www.free-ebooks-download.org Simpo PDF Merge and. Figure 23- 17) and click the Next button. CHAPTER 23 ■ ADO .NET PART II: THE DISCONNECTED LAYER 821 8849CH 23. qxd 10/22/ 07 1 :54 PM Page 821 www.free-ebooks-download.org Simpo PDF Merge and Split