Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

Figure 13.18: U se the Add Reference dialog to add the System.EnterpriseServices library to the project.

In the web service’s class module, add using directives to the System.Data.SqlClient and System.EnterpriseServices namespaces. Change the name of the class to Transaction. Add two private methods, InsertMoneyIn and InsertMoneyLog, which insert into the respective tables.

Next, add a web method, UpDateTables, that invokes both private methods:

public void UpDateTables(string inID, float amount, string desc) { InsertMoneyIn (inID, amount, desc);

InsertMoneyLog (inID, amount, desc);

}

Mark this web method with an attribute, TransactionOption, set to TransactionOption

.RequiresNew:

[WebMethod (Description = "Demonstrates two-stage commit", TransactionOption = TransactionOption.RequiresNew)]

The TransactionOption web method attribute nominally has several possible values, but really only two relevant ones: TransactionOption.Disabled, which is the default, is equivalent

to omitting the attribute and means that transaction management has been turned off; and TransactionOption.RequiresNew, which turns on transaction management.

The code for the web service is shown in Listing 13.8.

Listing 13.8: Inserting Two Tables within a Transaction

using System.Data.SqlClient; using System.EnterpriseServices;

...

[WebService(Namespace="http://bearhome.com/webservices/")] public class Transaction : System.Web.Services.WebService {

...

[WebMethod (Description = "Demonstrates two-stage commit", TransactionOption = TransactionOption.RequiresNew)]

public void UpDateTables(string inID, float amount, string desc) { InsertMoneyIn (inID, amount, desc);

InsertMoneyLog (inID, amount, desc);

}

private void InsertMoneyIn (string inID, float amount, string desc) { string sqlStr = "INSERT MoneyIn (MoneyInID, Amount, Description) " +

"VALUES (' + inID + "', " + amount.ToString() + ", '" + desc + "')"; SqlConnection myConnection = new SqlConnection("data source=SQLSERVER;"

+

"initial catalog=Deposits;password=harold;user id=sa"); myConnection.Open();

SqlCommand insert = new SqlCommand(sqlStr, myConnection); insert.ExecuteNonQuery();

if (myConnection.State == ConnectionState.Open) myConnection.Close();

}

private void InsertMoneyLog (string inID, float amount, string desc) { string sqlStr = "INSERT MoneyLog (MoneyInID, Amount, Description)" + " VALUES (' + inID + "', " + amount.ToString() + ", '" + desc + "')";

SqlConnection myConnection = new SqlConnection("data source=SQLSERVER;"

+

"initial catalog=Deposits;password=harold;user id=sa"); myConnection.Open();

SqlCommand insert = new SqlCommand(sqlStr, myConnection); insert.ExecuteNonQuery();

if (myConnection.State == ConnectionState.Open) myConnection.Close();

}

...

If you run the web service, the test page generated for it shows, as one would expect, a single web service (Figure 13.19).

Figure 13.19: The generated test page for the web service shows one method.

Use the test pages to enter some values (Figure 13.20) and click Invoke.

Figure 13.20: To test the service, enter values and click Invoke.

You should verify that the test data has been added to the database tables (as shown in Figure 13.21 in Server Explorer)—demonstrating that both private methods that insert data into the tables are working.

Figure 13.21: You can use Server Explorer to verify that the data has been added to the tables.

Testing the Transaction Web Service

Now let’s see whether transaction management is really working! Modify the second private method in the web service, the one that inserts into the MoneyLog table, so that it throws an exception (and no longer works), as shown in Listing 13.9.

Listing 13.9: Throwing an Exception in the Second Insert

private void InsertMoneyLog (string inID, float amount, string desc) { string sqlStr = "INSERT MoneyLog (MoneyInID, Amount, Description)" +

" VALUES (' + inID + "', " + amount.ToString() + ", '" + desc + "')"; SqlConnection myConnection = new SqlConnection("data source=SQLSERVER;"

+

"initial catalog=Deposits;password=harold;user id=sa"); myConnection.Open();

SqlCommand insert = new SqlCommand(sqlStr, myConnection); try {}

finally {

throw new Exception ("This is a nasty exception!");

}

insert.ExecuteNonQuery();

if (myConnection.State == ConnectionState.Open) myConnection.Close();

}

With the code that sabotages the second insert in place, run the project’s test pages again, and enter some more data (Figure 13.22).

Figure 13.22: With the code that throws the exception in the second insert in place, try it with new data.

Click Invoke. If all goes according to plan, an exception will be thrown, and you’ll get a message that looks something like this (I’ve excerpted parts):

System.Web.HttpException: Exception of type System.Web.HttpException was thrown. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Exception: This is a nasty exception! at SybexC47.Transaction.InsertMoneyLog(String inID, Single amount, String desc) in c:\inetpub\wwwroot\sybexc47\transaction.asmx.cs:line 78 at SybexC47.Transaction.UpDateTables(String inID, Single amount, String desc) in c:\inetpub\wwwroot\sybexc47\transaction.asmx.cs:line 55 ---

End of inner exception stack trace ---

...

at System.Web.Util.TransactedInvocation.ExecuteTransactedCode()

--- End of inner exception stack trace ---

at System.Web.Util.Transactions.InvokeTransacted(TransactedCallback callback, TransactionOption mode, Boolean& transactionAborted)

at System.Web.Util.Transactions.InvokeTransacted(TransactedCallback callback, TransactionOption mode)

at System.Web.Services.Protocols.WebServiceHandler.InvokeTransacted() at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

The key things to note are the methods I’ve placed in bold type, which indicate that transaction management was started, a transaction aborted, and the transaction rolled back. You should also verify that the test data has not actually been added to the first table, MoneyIn (Figure 13.23).

Figure 13.23: You can verify that the test data has not been inserted into either table.

Note One of the steps in the transaction doesn’t have to do something as drastic as throwing an exception to cause the step (and the transaction) to fail. For example, an attempt

to use a duplicate primary key will cause the first insertion to fail, even if the primary key used in the second insertion is not a duplicate—thus rolling back the entire transaction.

Exposing a Private Message Queue Using a Web Service

In Chapter 11, “Messaging,” I showed you how to use message queues to create distributed applications. These distributed applications could start—and stop—activities by themselves sending and receiving messages.

I did note in Chapter 11 that a drawback to using the messaging application included with Windows as part of an application’s architecture is that it limits distributed applications to those running on Windows. This objection can easily be overcome by exposing messaging as a web service. In fact, as the example in the section shows, it’s extremely easy to expose private—or public—message queues as a web service.

Warning Obviously, there are security perils involved in exposing something like a message queue in any way—including as a web service. Careful consideration of security issues should take place before any distributed architecture, or any architecture that exposes system internals, is used.

To start, create a new ASP.NET Web Service project. Name the web service class Mq, and add two web methods. These web methods will operate on a private queue named "WebServices." SendMessage pushes a message onto the queue, and GetMessage pops the message queue. The code for the web service is shown in Listing 13.10 (for a more complete explanation of programming message queues, please refer to Chapter 11).

Listing 13.10: Exposing a Message Queue as a Web Service

using System.Messaging;

...

[WebService(Namespace="http://bearhome.com/webservices/")] public class Mq : System.Web.Services.WebService {

...

[WebMethod (Description = "Pushes a message on the queue")] public void SendMessage(string theText, string subject){

string qpath = @".\Private$\WebServices";

if (!MessageQueue.Exists(qpath)) MessageQueue.Create (qpath);

MessageQueue theQ = new MessageQueue(qpath); theQ.Formatter = new BinaryMessageFormatter(); theQ.Send (theText, subject);

}

[WebMethod (Description = "Pops a message off the queue")] public string GetMessage(){

string qpath = @".\Private$\WebServices"; if (!MessageQueue.Exists(qpath))

MessageQueue.Create (qpath); MessageQueue theQ = new MessageQueue(qpath);

theQ.Formatter = new BinaryMessageFormatter(); System.Messaging.Message msg = theQ.Receive(new TimeSpan (0,0,10)); return msg.Label + ": " + msg.Body.ToString();

}

...

If you run the project, you can use the generated test pages to see whether the SendMessage web method actually places a message on the queue (Figure 13.24).

Figure 13.24: The generated test pages allow you to test pushing a message on the queue.

You can open Server Explorer and expand the message queuing nodes to verify that the message was placed on the queue by the web method:

It’s also worth running the test page for the GetMessage web method to verify that the message is popped off the front of the queue. As you can see in Figure 13.25, the return string from the web method is the subject concatenated with the body of the message.

Nothing could be simpler than writing a client app that uses the web service to implement messaging (you need, of course, to include as usual a reference to the web service).

The code shown in Listing 13.11 uses the message-queuing web service to place a message on the queue when the Send button is clicked. When the Receive button is clicked, the message that is currently at the head of the queue is popped off and displayed in the title bar of the application (Figure 13.26).

Figure 13.25: The generated test pages let you check that you can pop messages off the queue.

Figure 13.26: It’ s easy to use the web service to access the private message queue. Listing 13.11: Consuming the Message Queue Service

private void btnSend_Click(object sender, System.EventArgs e) { webservice.Mq ws = new webservice.Mq();

ws.SendMessage (txtText.Text, txtSubject.Text);

}

private void btnReceive_Click(object sender, System.EventArgs e) { webservice.Mq ws = new webservice.Mq();

this.Text = ws.GetMessage();

}

Getting the Picture with TerraService

Microsoft’s TerraServer database is partially intended to demonstrate the scalability of Microsoft’s database products, since it contains terabytes of aerial and satellite image data supplied by the United States Geological Survey (USGS) and others. The site www.terraserver.com (Figure 13.27) is a portal primarily intended to enable subscription access to this data.

Figure 13.27: TerraServer.com is a “vortal,” or vertical portal, primarily offering subscription access to satellite and topographic imagery.

In addition to the TerraServer portal, much of the data—meaning aerial photographs, relief maps, and topographic maps—is available as a free web service. You can add a web reference to the TerraService web service to one of your projects, by choosing Project ?Add Web Reference. In the Add Web Reference dialog, if you enter the URL http://terraservice.net/TerraService.asmx , the WSDL "table of contents"—showing the web methods available— will be displayed (Figure 13.28).

Figure 13.28: The web service WSDL page shows the TerraService methods available and provides access to XML documentation.

Click Add Reference to add a reference to the TerraService web service in your project.

Although XML documentation is available through the Add Web Reference dialog, it is still the case that the hardest thing about working with TerraServer is discovery—figuring out what the various methods do and what they return. Your best tool in this discovery process, once you have added the web reference, is the Object Browser. As you can see in Figure 13.29, using the Object Browser you can determine the parameter and return types of all available web methods.

Figure 13.29: The Object Browser is the best tool in our arsenal for figuring out how the members of the TerraService work.

The user interface for the sample TerraService client application—with an aerial view of downtown Seattle—is shown in Figure 13.30.

Figure 13.30: The drama of Seattle’s lake- and-ocean setting becomes apparent from this aerial view.

This interface lets the user choose a map by city and state or by lat/lon (latitude and longitude) coordinate pairs. “But wait just a second,” you may be saying to yourself right about now. “I’d like to see my house from above. I know the address and ZIP code, but how do I find its lat/lon coordinates?”

As you’ll see in a bit, in order to display a city map, the TerraService methods must be used to calculate the lat/lon value of the city center. I’ve set the user interface of this sample application to display those lat/lon values—so one way you can go about it is to enter your city, read the lat/lon values for the city, and interpolate from there.

It’s probably both more precise and easier to go to MapBlast (www.mapblast.com) and enter your address. Create a map based on the address, and then click the button to print the map, and view a printable version of the map. Lat/lon coordinates will be displayed at the bottom of the printable map.

Whichever way you do it, it’s easy to use the TerraService client to display aerial views of places you care about (my home is at the center of the aerial photo shown in Figure 13.31).

Figure 13.31: Using latitude/longitude coordinates, you can zoom in on areas of particular interest (this photo is centered on the author’s home).

In addition to where, the user gets to choose whether an aerial photograph or a topographic map is displayed. Figure 13.32 shows the client application displaying a topographic map.

Figure 13.32: The TerraService returns topographic maps as an alternative to aerial photos.

As a preliminary to implementing the TerraService client application as a Windows forms project, you should include using directives to System.IO and System.Drawing.Imaging in the form class module:

using System.Drawing.Imaging; using System.IO;

I used a PictureBox control to display the image returned by the TerraServer web service, so the issue is setting the Image property of the PictureBox control. Listing 13.12 shows the portion of the code that manages the user interface, gathers user choices, calls the display method, and handles exceptions. You should know that in addition to the code shown in