Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 68 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
68
Dung lượng
485,04 KB
Nội dung
Disconnected Data 55 Imagine we have to update the Products table. (The actual algorithm we're going to put together will work on any DataSet, not just one drawn from the Products table.) Here's what we'll do: ❑ Create a new method called SetProductDetails on ProviderConnection. This method will accept a DataSet of rows drawn from the Products table. This will be known as the "Changed DataSet". ❑ We'll examine each row in the Changed DataSet in turn, looking for ones that have their RowState property set to Modified. ❑ When we find one, we'll get the same product back from the database. This time, however, we'll keep the SqlDataAdapter around and keep it bound to the DataSet. This new DataSet will be called the "Master DataSet". ❑ All of the columns in the applicable row in the Changed DataSet will be copied to the matching column in the Master DataSet. ❑ We'll use the SqlDataAdapter object's Update method to make the changes to the database itself. This technique will work whether the Changed DataSet is passed directly to DirectConnection or through RemoteConnection and the Web Service. The only drawback is that we have to create two SqlDataAdapter objects whereas, if we only had to deal with a direct connection, we'd need just one. Building "SetProductDetails" The first thing we need to do is add SetProductDetails to the abstract ProviderConnection object. Try It Out – Building "SetProductDetails" 1. Open the code editor for ProviderConnection. Add this method: ' GetSuppliers - get the entire supplier list Public MustOverride Function GetSuppliers() As DataSet ' SetProductDetails - set the details for products Public MustOverride Sub SetProductDetails(ByVal products As DataSet) End Class 2. Open RemoteConnection and add "stub" method. As before, we'll come back and fill this in later. Public Overrides Sub SetProductDetails(ByVal products As System.Data.DataSet) End Sub 3. Open DirectConnection and add this method: Chapter 14 56 ' SetProductDetails - save changes to changed products Public Overrides Sub SetProductDetails(ByVal products As System.Data.DataSet) SaveChanges("ProviderGetProductDetails", "@productId", products) End Sub 4. Then, add the SaveChanges method. ' SaveChanges - save changes to changed rows Protected Sub SaveChanges(ByVal selectStoredProc As String, _ ByVal selectParamName As String, _ ByVal changedDataSet As DataSet) ' Need to hold a database connection Dim connection As New SqlConnection(Provider.DbString) connection.Open() ' Go through each row in the master dataset Dim changedRow As DataRow For Each changedRow In changedDataSet.Tables(0).Rows ' Has it changed? If changedRow.RowState = DataRowState.Modified Then ' Get the id of the changes item Dim changedId As Integer = changedRow.Item(0) ' Get the master row by using the adapter Dim adapter As SqlDataAdapter = _ GetDataAdapter(connection, selectStoredProc, _ selectParamName, changedId) ' Create a command builder and bind it to the adapter Dim builder As New SqlCommandBuilder(adapter) ' Fill a new dataset Dim masterDataSet As New DataSet() adapter.Fill(masterDataSet) ' Get the row from this dataset Dim masterRow As DataRow = masterDataSet.Tables(0).Rows(0) ' Copy the changes from one to the other Dim dataValue As Object, index As Integer index = 0 For Each dataValue In changedRow.ItemArray masterRow.Item(index) = dataValue index += 1 Next ' Tell the adapter to update adapter.Update(masterDataSet) Disconnected Data 57 End If Next ' Close the connection connection.Close() End Sub 5. Open the Form Designer for Form1 and add a new button control next to the Load button. Change the Name property of the new button to btnSave. 6. Double-click on the Save button to create a new Click event handler. Add this code: Private Sub btnSave_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSave.Click ' Save changes Provider.Connection.SetProductDetails(ProductDataSet) ' Report the save MsgBox("The changes have been saved.") End Sub How It Works We're going to hold off explaining how the code works until we can actually run SetProductDetails, which we'll do in a short while. Chapter 14 58 Testing The Changes Before we run the project, we have to make sure that a primary key has been defined on the Products table. Without a primary key, SqlCommandBuilder will be unable to form the appropriate query to make the database changes. A primary key is necessary as the SqlCommandBuilder uses this information to generate the necessary SQL WHERE clause. Try It Out – Checking the Primary Key and Testing the Code 1. Using the Server Explorer, find the Products table item within NorthwindSQL. 2. Right-click on Products and select Design Table. 3. If the ProductID column does not have a small key icon in the selection margin, right-click ProductID and select Set Primary Key. You should end up with something like this: 4. Select File | Save Products from the menu to save the changes to the definition. Close down the definition window. 5. Click on the Products table in Server Explorer once more, and this time select Retrieve Data From Table. The first item should list a product with ID of 1 and a name of Chai. 6. Run the project. Click the Load button to load the product from the database and change the name to Chai Tea. Disconnected Data 59 7. Click the Save button. You see a message box telling you that the changes have been saved. 8. Flip back to Visual Studio and find the listing of rows from the Products table again. Right- click on any column in any one of the rows and select Run. You should now see that the underlying database data is now the same as the values entered into the DataGrid. How It Works Whenever changes are made to the edit control, the related DataSet is automatically updated. We hold the DataSet containing the products for editing in the ProductDataSet member. When the Save button is clicked, we pass this DataSet over to the SetProductDetails member of the current ProviderConnection object, in this case DirectConnection. ' Save changes Provider.Connection.SetProductDetails(ProductDataSet) ' Report the save MsgBox("The changes have been saved.") SetProductDetails defers processing of the changes to an internal helper method called SaveChanges. This method is a general-purpose function that isn't just tied to working with DataSets drawn from the Products table. ' SetProductDetails - save changes to changed products Public Overrides Sub SetProductDetails(_ ByVal products As System.Data.DataSet) SaveChanges("ProviderGetProductDetails", "@productId", products) End Sub Let's take a close look at SaveChanges. The first thing we need to do is establish a connection to the database. Chapter 14 60 Dim connection As New SqlConnection(Provider.DbString) connection.Open() Once we have the connection, we need to walk through each of the rows in the first table in the changedDataSet. We assume that the DataSet we've been given only supports a single table. Remember that changedDataSet is actually the same DataSet object that the DataGrid used for its binding so, in our case, it's only going to contain a single row. Preferably, we want the Product Editor application to handle multiple products. This method is prepared for the eventuality that we supply a list of multiple products. ' Go through each row in the master dataset Dim changedRow As DataRow For Each changedRow In changedDataSet.Tables(0).Rows For each row, we check it to see if it has been modified. Notice we don't do anything if the row has been deleted or added (both of which we can check for using RowState). ' Has it changed? If changedRow.RowState = DataRowState.Modified Then If the row has changed, we use the first column of the row to find the ID of the item that has been changed. In our case, this will be the ProductID. ' Get the id of the changes item Dim changedId As Integer = changedRow.Item(0) When we called SaveChanges, we provided the name of the stored procedure used to get the row from the database in the first place ("ProviderGetProductDetails"), and also the name of the sole parameter on this stored procedure ("@productId"). GetDataAdapter will return a SqlDataAdapter object that is able to populate a DataSet with whatever is currently stored in the database for the provided ID. ' Get the master row by using the adapter Dim adapter As SqlDataAdapter = _ GetDataAdapter(connection, selectStoredProc, _ selectParamName, changedId) In order to update the database, we need a SqlCommandBuilder. This object is capable of automatically generating the SQL needed to update the database. ' We need to create a command builder and bind it Dim builder As New SqlCommandBuilder(adapter) The SqlDataAdapter can then be used to fill a new DataSet with whatever value is currently stored in the database. We also get the first row from the first table in this DataSet and this references the same product that the current value of changedRow references. Disconnected Data 61 ' Fill a new dataset Dim masterDataSet As New DataSet() adapter.Fill(masterDataSet) ' Get the row from this dataset Dim masterRow As DataRow = masterDataSet.Tables(0).Rows(0) Once we have both rows, we copy the changed values into values stored against masterRow. ' Copy the changes from one to the other Dim dataValue As Object, index As Integer index = 0 For Each dataValue In changedRow.ItemArray masterRow.Item(index) = dataValue index += 1 Next At this point, what we effectively have is a copy of the new data that we were given but, this time, we have a SqlDataAdapter object that knows how to commit the changes to the database. ' Tell the adapter to update adapter.Update(masterDataSet) End If As the method can handle multiple rows, we keep looping and close the connection when we are finished. Next ' Close the connection connection.Close() End Sub Saving Changes over the Web Service To complete the functionality that we're going to explore with this application, we need to prove that we can save changes through the Web Service. Try It Out – Saving Changes over the Web Service 1. Open the Web Service project. Like we did before, delete the reference to Northwind Provider and add it again. Without this step, the service won't know anything about our new SetProductDetails method. 2. Open the code viewer for ProviderService.asmx. Add this new method: Chapter 14 62 <WebMethod()> Public Sub SetProductDetails(ByVal products As DataSet) Provider.Connection.SetProductDetails(products) End Sub 3. Build the project. Unless you do this, the new SetProductDetails method will not be available to the client application. 4. Flip back to the Northwind Provider project. Using Solution Explorer, find the NorthwindService Web Service reference group. Right-click on it and select Update Web Reference. Without this step, Northwind Provider wouldn't know about the SetProductDetails method that we just added to the service. 5. Next, open the code editor for RemoteConnection. Locate the dummy implementation for SetProductDetails and add this code: Public Overrides Sub SetProductDetails(_ ByVal products As System.Data.DataSet) ' Get the service and call get suppliers Dim service As NorthwindService.ProviderService = GetService() service.SetProductDetails(products) End Sub 6. Run the project and check on the Use Service checkbox. Database requests should now be routed through the Web Service. Make a change to the product that you load, click the Save button and use the Server Explorer to make sure that the changes have "stuck", like we did before. How It Works Again, adding functionality to the Web Service is simply an issue of forwarding requests of the Web method to the existing method on DirectConnection. Dim service As NorthwindService.ProviderService = GetService() service.SetProductDetails(products) .NET handles passing the DataSet over the Web Service and so, when we receive it at the other end, we can process it as normal. Disconnected Data 63 Summary In this chapter, we saw a pretty cool technique for building a client application that is deployable both inside and outside of the company LAN. With .NET, a lot of the deployment hassles of traditional desktop applications go away, meaning that companies can return to building desktop applications with rich and powerful user interfaces, without having to decrease the functionality for use with Web browsers. Web applications do not benefit from the same, rich user interface controls that desktop applications like those built with Windows Forms do. We kicked off the application by introducing the concept of an access layer. Instead of connecting directly to the database, the application instead connects to this layer. The layer is able to "switch" between connecting directly to SQL Server Desktop Engine and connecting to a Web Service. This means that building the Web Service is simply a matter of creating a few methods that defer over to the existing access layer. Adding new methods to the layer is pretty trivial. In building the application, we saw how to use the DataGrid control to display and edit product information. We also provided separate windows for looking up and changing supplier information. Finally, we solved the problem of saving changes back into the database even though we didn't have a DataAdapter object handy. Exercises 1. What's the advantage of using the techniques that we've described in this chapter? 2. Why did we choose a Web Service as the alternative way of connecting to the database? 3. How did we detect if a database connection was available? 4. Why did we need to go through the complicated updating process that we saw in this chapter? Answers are available at http://p2p.wrox.com/exercises/. Chapter 14 64 [...]... using XML Albuquerque NM 87 110 USA 1107 6 6 25 20 0.25 1107 6 14 23.25 20... Dr. Albuquerque NM 87 110 USA 1107 6 6 25 20 0.25 3 Chapter 15 1107 6 14 23.25 20... this stage, we implement the RequestDetail object only and, when that's working as it should, we shall move on to the ResposeDetail object Try It Out – Building the Project 1 Open Visual Studio NET and create a new Visual Basic | Class Library project Call it NorthwindOrderGenerator 2 Using Solution Explorer, delete the automatically created Class1.vb Then right-click on NorthwindOrderGenerator, still... document and save it to disk Using Solution Explorer, right click on the NorthwindOrderGenerator solution object at the top of the tree and select Add | New Project 10 Case Study – B2B Application Integration using XML 15 Create a new Visual Basic | Windows Application project and call it Order Generator Test Client We now need to add a reference to the NorthwindOrderGenerator project so right click on... looking at the schema Defining the Schema In Chapter 13, we built a simple application that exported orders held in the Orders table from the database You'll remember that the DataSet object exported the data in this format: 1107 7 RATTC 1 1998-05-06T00:00:00.0000000+01:00 1998-06-03T00:00:00.0000000+01:00... WriteElementString, there are no similar methods for other data types As far as the XmlTextWriter is concerned, anything that gets written out is a string, because the underlying document is comprised of text As Visual Basic NET will implicitly convert data types by default, when we pass in an integer such as CustomerId, that value is converted to a string through an implicit call to CustomerId.ToString Once we've... (Northwind's) server for processing? There are many different techniques and we'll see some of them in this chapter Web Service If we were asked to recommend a ".NET way" of transferring the document, we'd probably say a Web Service We could configure an ASP.NET Web Service to run on a server and listen for incoming documents Once a document was received, it could then be processed We demonstrate this technique... Service here because it requires the server to run on a network with a Primary Domain Controller If you're particularly interested in this facility, try Wrox's Professional MTS & MSMQ Programming with VB and ASP (ISBN 18 6100 1460) Proprietary Another way of transferring documents would be to put together your own proprietary protocol and write your own server With this technique you have ultimate control,... be started directly, and both projects in the solution are class libraries Using Solution Explorer, right-click on the Automated Order Processor solution and select Add | Add New Project Add a new Visual Basic | Console Application project Call it Automated Order Processor Test Host 13 When the code editor for Module1 appears, add this code: Module Module1 Sub Main() ' Start the processor Dim processor... To rectify this, it would be better to make the elements "members" of the element, by placing them inside a node, like this: 1107 7 RATTC 1 1998-05-06T00:00:00.0000000+01:00 1998-06-03T00:00:00.0000000+01:00 2 . on to the ResposeDetail object. Try It Out – Building the Project 1. Open Visual Studio .NET and create a new Visual Basic | Class Library project. Call it NorthwindOrderGenerator. 2. Using Solution. selectParamName, changedId) In order to update the database, we need a SqlCommandBuilder. This object is capable of automatically generating the SQL needed to update the database. ' We need to create. chapter? 2. Why did we choose a Web Service as the alternative way of connecting to the database? 3. How did we detect if a database connection was available? 4. Why did we need to go through the complicated