Passing Data using a Web Service


I'm going to finish off this chapter with an elaborate example of a Web service. It will take the MaintAuthors detached database project example you created back in Chapter 12 and convert it to a Web service.

With this example, you will truly see a detached (figuratively speaking) database, where the client is on one system and the Web service (database) is located somewhere else on the Internet.

The Web service is made up of two methods: The first returns a DataSet of authors and the second takes in a DataSet of authors and updates the database based on the batched processes made by the client to the authors DataSet. You should note that this example takes no concurrency issues into consideration (i.e., what happens if multiple clients update the database via the multiple Web service instances at the same time?).

The Windows Form client application receives a DataSet of authors and then allows additions, updates, and deletions to the DataSet.

Returning a DataSet

The easier of the two Web service methods to implement relates to filling a DataSet of all authors and then sending the DataSet from the Web service to the consuming client (see Listing 15-11).

Listing 15-11: Building the Authors DataSet Web Service

start example
 using namespace System; using namespace System::Data; using namespace System::Data::SqlClient; using namespace System::Web; using namespace System::Web::Services; namespace AuthorWS {      [WebService(Namespace = S"http://contentmgr.com",      Description = S"A Web Service to handled the CRUD of the Authors Database")]      public __gc class AuthorWSClass : public WebService      {      private:          static String *connstring =                         S"server=localhost;uid=sa;pwd=;database=DCV_DB";      public:          [WebMethod(          Description = S"Method to retrieve All Authors from the database")]          DataSet *GetAuthors()          {               SqlConnection *connect;               SqlDataAdapter *dAdapt;               DataSet *dSet;               connect = new SqlConnection(connstring);               dAdapt = new SqlDataAdapter();               dAdapt->MissingSchemaAction = MissingSchemaAction::AddWithKey;               dAdapt->SelectCommand = new SqlCommand(                   S"SELECT AuthorID, LastName, FirstName FROM Authors",                   connect);               dSet = new DataSet();               dAdapt->Fill(dSet, S"Authors");               return dSet;          }          //...      }; } 
end example

As you can see, a Web service has no problems sending the complex DataSet object using SOAP. In fact, if it wasn't for the WebMethod attribute, this method would look like any other ADO.NET DataSet fill method.

One big difference, though, is that this method uses its own method scope version of the Connection, DataAdapter, and DataSet. The reason is that a Web service (unless otherwise specified using EnableSession property of the WebMethod attribute) is stateless. Basically, each time the Web service is called, it is from scratch. Thus, there is no need to have the Connection, DataAdapter, or DataSet stick around after the Web service method has finished. For this same reason, there is no reason to assign the InsertCommand, UpdateCommand, and DeleteCommand properties to the DataAdapter as they are not used in the method.

Inserting, Updating, and Deleting Rows in a DataSet

Inserting, updating, and deleting rows in a DataSet via a Web service is handled in virtually the same way as standard, nondistributed ADO.NET. The following UpdateAuthors() method (see Listing 15-11) is made up of code that is almost exactly the same as what you saw in Chapter 12.

Listing 15-11: Updating the Authors Database Web Service

start example
 using namespace System; using namespace System::Data; using namespace System::Data::SqlClient; using namespace System::Web; using namespace System::Web::Services; namespace AuthorWS {      [WebService(Namespace = S"http://contentmgr.com",      Description = S"A Web Service to handled the CRUD of the Authors Database")]      public __gc class AuthorWSClass : public WebService      {          //...          [WebMethod(Description =          S"Method to Commit changed made on client with Server database")]          void UpdateAuthors(DataSet *dSet)          {               SqlConnection *connect;               SqlDataAdapter *dAdapt;               connect = new SqlConnection(connstring);               dAdapt = new SqlDataAdapter();               dAdapt->MissingSchemaAction = MissingSchemaAction::AddWithKey;               dAdapt->InsertCommand =               new SqlCommand(S"INSERT INTO Authors (LastName, FirstName) "                               S"VALUES (@LastName, @FirstName)", connect);               dAdapt->InsertCommand->Parameters->Add(                       S"@LastName", SqlDbType::VarChar, 50, S"LastName");               dAdapt->InsertCommand->Parameters->Add(                       S"@FirstName", SqlDbType::VarChar, 50, S"FirstName");               dAdapt->UpdateCommand = new SqlCommand(                       S"UPDATE Authors SET LastName = @LastName, "                       S"FirstName = @FirstName"                       S"WHERE AuthorID = @AuthorID", connect);               dAdapt->UpdateCommand->Parameters->Add(                        S"@LastName", SqlDbType::VarChar, 50, S"LastName");               dAdapt->UpdateCommand->Parameters->Add(                        S"@FirstName", SqlDbType::VarChar, 50, S"FirstName");               dAdapt->UpdateCommand->Parameters->Add(                        S"@AuthorID", SqlDbType::Int, 4, S"AuthorID");               dAdapt->DeleteCommand = new SqlCommand(                        S"DELETE FROM Authors WHERE AuthorID = @AuthorID", connect);               dAdapt->DeleteCommand->Parameters->Add(                        S"@AuthorID", SqlDbType::Int, 4, S"AuthorID");               dAdapt->Update(dSet, S"Authors");          }      }; } 
end example

I'm sure you are seeing the pattern here. Distributed code using Web services is usually very close to, if not the same as, its nondistributed equivalent. The only real difference is that the class state is not maintained. Therefore, you have to be careful about global and class variables.

Unlike the plain ADO.NET version in Chapter 12, the Web service creates a new version of the DataAdapter each time a DataSet update is required. The reason, as I stated previously, is that the Web service is stateless, so on the call to the AuthorUpdate() method, no DataAdapter object exists. Having a new or different DataAdapter from the one when the DataSet was created is not an issue, because a DataAdapter is not strongly linked to the DataSet it is supporting. In fact, so long as the database schema is the same, DataSets are interchangeable as far as DataAdapters are concerned. As you will see later, the DataSet of the Update process can be a subset of the one sent by the GetAuthors() method, because only changed rows are contained within this DataSet.

What is neat about this method is that it can handle inserted, updated, and deleted records, all in a batch-like manner, instead of requiring a separate method for each of these process types.

Caution

To simplify this example, I didn't add any code to handle database concurrency.

Authors DataSet Processing Web Service Client

In truth, there is little reason to include this section in the chapter other than to show that very little has changed in the Web service client application when you compare it to the ADO.NET example in Chapter 12. Listing 15-12 has been included so that you can compare it to the source code of the MaintAuthors example in Chapter 12.

Listing 15-12: Web Server Version of the MaintAuthors Application

start example
 #include "AuthorWS.h" namespace MaintAuthors {      using namespace System;      using namespace System::ComponentModel;      using namespace System::Collections;      using namespace System::Windows::Forms;      using namespace System::Data;      using namespace System::Data::SqlClient;      using namespace System::Drawing;      using namespace AuthorWS;      public __gc class Form1 : public System::Windows::Forms::Form      {      public:          Form1(void)          {              InitializeComponent();              authors = new AuthorWSClass();              dSet = authors->GetAuthors();              DataTable *dt = dSet->Tables->Item["Authors"];              if (dt == 0)                  throw new Exception(S"No Authors Table");              IEnumerator *Enum = dt->Rows->GetEnumerator();              while(Enum->MoveNext())              {                   DataRow *row = dynamic_cast<DataRow*>(Enum->Current);                   lbAuthors->Items->Add(ListBoxItem(row));              }              CurrentAuthorID          }      protected:          void Dispose(Boolean disposing)          //...          DataSet *dSet;          Int32 CurrentAuthorID;          AuthorWSClass *authors;          void InitializeComponent(void)          //...Not shown to save space      private:          String *ListBoxItem(DataRow *row)          //...Same as Chapter 12      private:          System::Void bnAdd_Click(System::Object * sender, System::EventArgs * e)          {              if (tbFirstName->Text->Trim()->Length == 0 ||                  tbLastName->Text->Trim()->Length == 0)                  return;              DataTable *dt = dSet->Tables->Item["Authors"];              DataRow *row = dt->NewRow();              row->Item[S"FirstName"] = tbFirstName->Text;              row->Item[S"LastName"] = tbLastName->Text;              dt->Rows->Add(row);              lbAuthors->Items->Add(ListBoxItem(row));              tbFirstName->Text = S"";              tbLastName->Text = S"";          }      private:          System::Void bnUpdate_Click(Object * sender, System::EventArgs * e)          {              if (CurrentAuthorID < 0)                  return;              DataTable *dt = dSet->Tables->Item["Authors"];              DataRow *row[] =              dt->Select(String::Format(S"AuthorID={0}", __box(CurrentAuthorID)));              row[0]->Item[S"FirstName"] = tbFirstName->Text;              row[0]->Item[S"LastName"] = tbLastName->Text;              lbAuthors->Items->Insert(lbAuthors->SelectedIndex,                                         ListBoxItem(row[0]));              lbAuthors->Items->RemoveAt(lbAuthors->SelectedIndex);          }      private:          System::Void bnDelete_Click(Object * sender, System::EventArgs * e)          {              if (CurrentAuthorlD < 0)                  return;              DataTable *dt = dSet->Tables->Item["Authors"];              DataRow *row[] =              dt->Select(String::Format(S"AuthorID={0}", _box(CurrentAuthorlD)));              row[0]->Delete();              lbAuthors->Items->RemoveAt(lbAuthors->SelectedIndex);          }      private:          System::Void lbAuthors_SelectedIndexChanged(System::0bject * sender,                                                          System::EventArgs * e)          //...Same as Chapter 12      private:          System::Void bnCommit_Click(Object * sender, System::EventArgs * e)          {              authors->UpdateAuthors(dSet->GetChanges());              dSet->AcceptChanges();              lbAuthors->Items->Clear();              DataTable *dt = dSet->Tables->Item["Authors"];              IEnumerator *Enum = dt->Rows->GetEnumerator();              while(Enum->MoveNext())              {                  DataRow *row = dynamic_cast<DataRow*>(Enum->Current);                  lbAuthors->Items->Add(ListBoxItem(row));              }              CurrentAuthorID = -1;          }      private:          System::Void bnRollback_Click(Object * sender, System::EventArgs * e)          {               dSet->RejectChanges();               lbAuthors->Items->Clear();               DataTable *dt = dSet->Tables->Item["Authors"];               IEnumerator *Enum = dt->Rows->GetEnumerator();               while(Enum->MoveNext())               {                   DataRow *row = dynamic_cast<DataRow*>(Enum->Current);                   lbAuthors->Items->Add(ListBoxItem(row));               }               CurrentAuthorID          }      }; } 
end example

As you can see, the code is the same except that the ADO.NET DataAdapter and DataSet logic has been removed. In actuality, this logic should probably have been moved to its own class in the example in Chapter 12, but this was not done because it simplifies the code listing.

Figure 15-10 shows the Web service version of MaintAuthors.exe in action. Those of you looking for differences between this and the original version in Chapter 12 won't find any.

click to expand
Figure 15-10: Web service version ofMaintAuthors




Managed C++ and. NET Development
Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
ISBN: 1590590333
EAN: 2147483647
Year: 2005
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net