Tải bản đầy đủ (.pdf) (90 trang)

Beginning ASP.NET 1.1 with Visual C# .NET 2003 phần 8 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.51 MB, 90 trang )


When you use Web services within your ASP.NET logic, SOAP is used as the default protocol. Although
it seems a little more bulky than the other options, it's the only mechanism that makes it possible to use
Web methods directly, in a flexible manner , and seamlessly from within the code.
Building an ASP.NET Web Service
Let's take a more detailed look at putting a Web service together. We'll also begin to explore the possible
uses for Web services.
You can define a Web service by simply writing a few lines of code and placing them inside a file with a
.asmx extension. This extension tells Web Matrix that a Web service is being defined. You can create a
Web service just like a standard ASP.NET page, using Web Matrix (as we did earlier in the chapter) or
any text editor. A Web service must contain four essential parts:
❑ Processing directive.
❑ Namespaces.
❑ Public class.
❑ Web methods.
Let's look at each of these in turn.
Processing Directive
Within the empty ASMX file (the required file type for an ASP.NET Web service page), you must let the
Web server know that you're creating a Web service. To do this, enter a directive at the top of your page
with the following syntax (in fact, Web Matrix creates this automatically based on the information
submitted in the startup dialog):
<%@ WebService Language="language" Class="classname"%>
This statement appears at the top of an ASP.NET source file to tell the .NET Framework about any
settings or constraints that should be applied to whatever object is generated from the file. In this case,
the directive tells the compiler the language in which the Web service is written, and the name of the
class in which it is defined. This class might reside in the same file, or within a separate file (which must
be in the
\bin directory, immediately beneath the Web application root in which the Web service lives).
Namespaces
You can make use of other file's logic within your ASMX page by specifying appropriate namespaces. In
this case, you can use the C#


using command:
using System;
using System.Web.Services;
using System.Xml.Serialization;
Web services require importing these namespaces as an absolute minimum, because they contain the
classes needed for Web services to handle network connection issues and other OS-related tasks.
603
Web Services
Public Class
A public class acts as a container for the methods in our Web service:
public class ClassName
{

}
Essentially, we're just defining an object whose methods will be exposed over the Web. This will
ultimately allow us to make remote method calls over the Internet. To our server, these calls look like
method calls to the same machine where the consuming application resides.
Web Methods
The methods exposed for consumption over the Internet are known as Web-callable methods or simply
Web methods. By definition, a Web service will expose one or more Web methods – of course it can have
other non-Web methods as well, and these can be protected as needed so that consumers cannot use
them directly. The syntax varies slightly depending upon the language used, but they all tend to follow a
similar structure. In C#, it is as follows:
[WebMethod]
public string Hello(string strName)
WebMethod
attribute can take parameters of its own. Thus you can set various properties that modify the
activity of the attribute. This allows us to customize our Web methods in various ways; for example, we
can use
CacheDuration to set the number of seconds for which the WebMethod will cache its results. If a

consumer requests a result from the Web Service within the time specified in this attribute,
WebMethod
will retrieve the cached copy of these values instead of retrieving them from the original source:
[WebMethod(CacheDuration:= 5)]
public string Hello(string strName)
{

For more information on WebMethod attributes, such as CacheDuration, please visit:
www.microsoft.com/library/default.asp.
When you build a Web service, a lot of time will be spent creating a Web method for the Web service. It
is possible to include more than one Web method in an ASMX file, as you'll see in the next example.
We only place the WebMethod declaration before the functions that we wish to expose
to consumers. Those without this declaration cannot be seen.
The name of this class is effectively the name of the Web service. Therefore, it should
correspond to the Class value specified in the processing directive.
604
Chapter 16
Try It Out Creating a Web Service with Multiple Web Methods
This Web service contains four Web methods that convert inches to centimeters, centimeters to inches,
miles to kilometers, and kilometers to miles.
1. Create an XML Web service in Web Matrix called measurementconversions.asmx and enter
MeasurementConversions as the class, Ch16 as the namespace, and add the following code:
<%@ WebService language="C#" class="MeasurementsConversions" %>
using System;
using System.Web.Services;
using System.Xml.Serialization;
public class MeasurementsConversions
{
[WebMethod(Description="Convert Inches To Centimeters")]
public decimal InchesToCentimeters(decimal decInches) {

return decInches * 2.54m;
}
[WebMethod(Description="Convert Centimeters to Inches")]
public decimal CentimetersToInches(decimal decCentimeters) {
return decCentimeters / 2.54m;
}
[WebMethod(Description="Convert Miles to Kilometers")]
public decimal MilesToKilometers(decimal decMiles) {
return decMiles * 1.61m;
}
[WebMethod(Description="Convert Kilometers to Miles")]
public decimal KilometersToMiles(decimal decKilometers) {
return decKilometers / 1.61m;
}
}
2. Call it up in your browser and you should see something like Figure 16-6:
Figure 16-6
605
Web Services
Let's look at the code for a moment. We'll get back to the testing of our Web service after this.
How It Works
In this example, we created a Web service that converts between Imperial (English) measurements and
Metric measurements. The first line tells us that the file is a Web service written in C#. We have a class
name of
MeasurementConversions that will be used by consumers to make references to the Web
service:
<%@ WebService language="C#" class="MeasurementsConversions" %>
Next, we import the namespace that allows us to refer to Web service objects without using fully
qualified names:
using System;

using System.Web.Services;
using System.Xml.Serialization;
We then name our class to match the processing directive class name. We'll need to know this when we
are ready to make remote calls to the Web service through a consumer:
public class MeasurementConversions
Finally, consider the actual Web methods. These are separate functions that can be called within a Web
service to return a result. The first Web method receives a Decimal value in inches and converts it to a
Decimal value in centimeters using the standard conversion formula. The second receives a Decimal in
centimeters and converts it to inches in the same manner:
[WebMethod(Description="Convert Inches To Centimeters")]
public decimal InchesToCentimeters(decimal decInches) {
return decInches * 2.54m;
}
[WebMethod(Description="Convert Centimeters to Inches")]
public decimal CentimetersToInches(decimal decCentimeters) {
return decCentimeters / 2.54m;
}
The third and fourth Web methods perform similar conversions from miles to kilometers and kilometers
to miles respectively:
[WebMethod(Description="Convert Miles to Kilometers")]
public decimal MilesToKilometers(decimal decMiles) {
return decMiles * 1.61m;
}
[WebMethod(Description="Convert Kilometers to Miles")]
public decimal KilometersToMiles(decimal decKilometers) {
return decKilometers / 1.61m;
}
We've now created a complete Web service by using the processing directive, adding namespaces, and
creating Web methods. Now the big question is 'How do we know it works?' It's time to put it through
its paces.

606
Chapter 16
Testing Your Web Service
To test Web services, all you need is an Internet connection and a browser. In the browser address bar,
just enter the URL of the Web service in the following format:
http://[path]/[webservice].asmx
The first time the Web service is accessed, the code will compile on the Web server, and a new browser
window will appear containing some very helpful diagnostic information. This
Web service Description
page allows us to impersonate a consumer and enter input values to send to the Web service. The page
contains the following information about the Web service:

Web method names: Names of the Web service's Web-callable functions.

Request parameters: The names of all the inputs that the Web service expects a consumer to
supply.

Response Type: The data type of the result sent by the Web service to a consumer (such as
integer, string, float, and object).
❑ Fields: These can be used to enter test values.
You'll also see the following message at the top of the test page:
The following operations are supported. For a formal definition, please review the Service Description.
The Service Description is a comprehensive technical description of all the functionality exposed by the
Web service. You'll be taking a closer look at it later on in the chapter. For the time being, we're only
interested in testing our Web service. Let's now go back and see what happens when we test our
measurementconversions Web service.
Try It Out Conversions Test Page
1.
Assuming your browser is still open, just click on the MilesToKilometers hyperlink and enter a
test value of 60 in the decMiles value field, as shown in Figure 16-7:

Figure 16-7
607
Web Services
2. Click Invoke, and a new browser window appears, containing our result in kilometers in XML
format. This is shown in Figure 16-8:
Figure 16-8
3. In the original .asmx page, click on the word here at the top of the test page, and you'll return to
the original test screen. You can now repeat this procedure for the other methods shown on the
page.
How It Works
When we browse to the test page, we see a screen containing the name of our Web service and
underneath it, a list of the methods that it exposes. These method names are hyperlinks. When we click
on
MilesToKilometers, the Web method test section will appear in the browser window. We are given the
name of the parameter (
decMiles), and an associated field to enter the test value.
Once the value is entered, we can press the
Invoke button to execute the Web method. By doing this, we
are impersonating a consuming application. The entered test value (
60) is passed using HTTP as a
request, to the
MilesToKilometers Web method. The value will be multiplied by 1.61 and returned as
a decimal. The result is in XML.
You might say, "Sure, our test page tells us what the Web service's expectations are. But how would a
consumer know what they are?" This consumer might not necessarily be another user, it could be an
application and then the expectations need to be explicitly defined.
The next section discusses how to know what a Web service requires, what it produces, and how a
consumer can communicate with it.
Using Your Web Service
As you've learned, it's essential for consumers to know what parameters to send to a Web service and

what values to expect it to return. To accomplish this, a Web service Description Language (WSDL) file is
used. This is an XML file that defines how the interaction between a Web service and its consumer will
occur. WSDL is a standard managed by the W3 standards organization, and you can find more details
about it at
/>The impact of this WSDL standard is enormous. WSDL is able to define all the interactions of a Web
service regardless of whether the service is running in ASP.NET or Java, and regardless of whether it is
running on Windows or UNIX.
The data type for MilesToKilometers is a decimal. This is the value that our
measurementconversions Web service expects from a consumer.
608
Chapter 16
It means that in future you won't need to be concerned with whether our services, or languages, are
compatible across platforms. This would allow us to concentrate on the real issue of writing robust and
functional code. WSDL will take care of declaring the interaction for us.
For instance, if a Web service expects two specific parameters and returns a single value, the WSDL
defines the names, order, and data types of each input and output value. Since we know where to find
the Web service using its URL, we don't need to know the physical location or the internal logic of the
Web service. With WSDL, we have all the information necessary to begin making use of the Web service
functionality within our applications. It's really that simple!
Let's take a quick look at what a WSDL contract looks like using our
MeasurementConversion Web
service.
Try It Out Viewing the WSDL Contract
1.
Enter the path http://localhost/measurementconversions.asmx in your browser's address bar and
click on the
Service Description hyperlink at the top of the page. You should see a screen similar
to Figure 16-9:
Figure 16-9
How It Works

As you can see, there's a lot of information in here and this is just the collapsed view! Our Web method
message names along with the various HTTP
GET, HTTP POST, and SOAP message structures are
displayed. These message formats contain the requirements for a consumer to know what parameters
are needed to communicate with a Web service using each message structure.
609
Web Services
At the top, the following declaration indicates that the WSDL file is in XML format:
<?xml version="1.0" encoding="utf-8" ?>
Below that declaration is the <definitions> element, which contains various namespaces. Most of
these namespaces make a reference to SOAP, which we discussed earlier. These must be included in the
file for SOAP to work correctly:
<definitions xmlns:http="
xmlns:soap="
xmlns:s=" xmlns:s0="
xmlns:soapenc="
xmlns:tm="
xmlns:mime="
targetNamespace="
xmlns=" />Next, the <types> element defines each of the data types that the Web service expects to receive and
return after completion. This is very complex, and almost a science in itself. It is written in XML Schema
Definition (XSD) language. You can't see the definitions in the screenshot as its section is collapsed (like
the others). All you need to do is click on the node in Internet Explorer in order to view them.
After this are the various one-way transmissions from a consumer to the Web service and back again.
Our Web method message names are in here, and the various SOAP message structures are laid out. For
example, on expanding the
<message> element, we can see the InchesToCentimeters Web method
message structures for SOAP:
<message name="InchesToCentimetersSoapIn">
<part name="parameters" element="s0:InchesToCentimeters" />

</message>
<message name="InchesToCentimetersSoapOut">
<part name="parameters" element="s0:InchesToCentimetersResponse" />
</message>:
In short, this file contains all of the information necessary to communicate with our Web service. Now
that you've seen the process of building and communicating with XML Web services in detail, let's create
something a bit more complex.
The next example will accept a value, and return a result using ADO.NET to retrieve data from an
Access database.
Try It Out ISBN Search Web Service
Let's create a Web service that returns the title of a book, based on an ISBN that the consumer provides.
This will allow our librarian to add a function on the library's Web page that enables users to search by
consuming this Web service.
This particular service will access a database of books. The database contains information on ISBN and
book titles. Once the details are received from the database, the results will be inserted into a
DataReader and returned to the consumer in XML.
610
Chapter 16
This example uses the Library.mdb Access database, which you can download along with the code
samples for this book from
www.wrox.com. You should ensure that the file is in the same location as the
Web service that you create.
1. Create an XML Web service called ISBN.asmx in Web Matrix, entering ISBN as the class name
and
Ch16 as the Namespace.
2. Add the following using statements to the beginning of the file:
<%@ WebService Language="C#" Class="ISBN" %>
using System;
using System.Web.Services;
using System.Xml.Serialization;

using System.Data;
using System.Data.OleDb;
3. Add the following code to enable the Web service:
public class ISBN : System.Web.Services.WebService
{
[WebMethod]
public string BookDetail(string strIsbn)
{
return GetBookDetails(strIsbn);
}
4. Enter the following code directly after the BookDetail Web method. This function performs the
database lookup and returns the book title string:
private string GetBookDetails(string strIsbn)
{
OleDbDataReader objLibraryDR = null;
OleDbConnection objLibraryConn = null;
OleDbCommand objLibraryCmd = null;
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Server.MapPath("Library.mdb") + ";";
string strSQL = "SELECT Title FROM Books WHERE ISBN = '" + strIsbn +
"'";
string strBookTitle;
objLibraryConn = new OleDbConnection(strConn);
objLibraryCmd = new OleDbCommand(strSQL, objLibraryConn);
objLibraryConn.Open();
objLibraryDR =
objLibraryCmd.ExecuteReader(CommandBehavior.CloseConnection);
if (objLibraryDR.Read())
{
strBookTitle = objLibraryDR[0].ToString();

}
else
{
strBookTitle = "Book not found in the database";
}
objLibraryDR.Close();
return strBookTitle;
}
}
611
Web Services
5. Once you have completed this code entry, test your Web service. Save the file, and then browse
to
http://localhost/ISBN.asmx.
6. Within the Isbn field, enter the ISBN 0764557076. A new browser window will appear,
containing XML shown in Figure 16-10:
Figure 16-10
How It Works
Our Web service provides what is technically known as a 'level of abstraction'. This means that the code
that does the work of finding our information isn't taken care of by the Web-callable
BookDetails
method. Instead, BookDetails calls another internal function that consumers can't see. This function,
GetBookDetails, does the work of finding the book information, and then returns it to BookDetails,
which returns it to us:
[WebMethod]
public string BookDetail(string strIsbn)
{
return GetBookDetails(strIsbn);
}
private string GetBookDetails(string strIsbn)

{

}
This is done because the job of the GetBookDetails function remains the same, regardless of the source
making the request. The same function may be called from a non-Web service source. Also, we certainly
wouldn't want to maintain two separate functions that do the same thing, the difference being only the
[WebMethod] declaration.
We're using ADO.NET to connect to the
Library.mdb database, retrieve a book title from its Books
table based on the ISBN, and store it in a string variable. Keeping the data request simple, we define a
connection string (
Conn), and then open the connection to the database (with LibraryConn):
objLibraryConn = new OleDbConnection(strConn);
objLibraryCmd = new OleDbCommand(strSQL, objLibraryConn);
objLibraryConn.Open();
612
Chapter 16
Using the LibraryCmd object, we execute the query for a specific ISBN, placing the results in the
LibraryDr DataReader. Then we close the connection:
objLibraryDR =
objLibraryCmd.ExecuteReader(CommandBehavior.CloseConnection);
We then check whether a row was returned, by calling the Read method of our DataReader,
LibraryDr. If it returns true, we take the first column (column zero, the Title column of the database)
from the
DataReader and place it into BookTitle. If it returns false, we know that the book was not
found, and we place a 'not found' message in the title value. Then we close our
DataReader and return
the book title string:
if (objLibraryDR.Read())
{

strBookTitle = objLibraryDR[0].ToString();
}
else
{
strBookTitle = "Book not found in the database";
}
objLibraryDR.Close();
return strBookTitle;
For more information on working with data sources, please refer to Chapters 8 and 9.
Consuming a Web Service
You've created some Web services from start to finish using a variety of technologies. The next step is to
understand how to include this functionality within a consumer application. To do this, you must first
create an interface that will allow the consumer to see all of the Web-callable methods and properties
exposed by the Web service. This saves the headache of ensuring that your parameters are the correct
type and having to create our own protocol request and response handlers. This interface is called a Web
service proxy.
How Does a Proxy Work?
A proxy resides on the consumer's machine and acts as a relay between the consumer and the Web
service. When building a proxy, we use a WSDL file (we'll examine the source shortly) to create a map
that tells the consumer what methods are available and how to call them. The consumer then calls the
Web method that is mapped in the proxy, which in turn, makes calls to the actual Web service over the
Internet. The proxy (and not the consumer) handles all of the network related work, the sending of data,
as well as managing the underlying WSDL. When we reference the Web service in the consumer
application, it looks as if it's part of the consumer application itself. Figure 16-11 illustrates this process:
613
Web Services
Figure 16-11
The function works as follows:
1. The application executes a function in the proxy code, passing any appropriate parameters to it,
without being concerned that the proxy is going to call a Web service.

2. The proxy receives this call, and formulates the request that will be sent to the Web service,
using the parameters the consumer has specified.
3. This function call is sent from the proxy to the Web service. This call can be within the confines
of the same machine, across a Local Area Network (LAN), or across the Internet. The method of
calling remains the same.
4. The Web service uses the parameters provided by the proxy to execute its Web-callable function
and build the result in XML.
5. The resulting data from the Web service is returned to the proxy at the consumer end.
6. The proxy parses the XML returned from the Web service to retrieve the individual values
generated. These values may be as simple as integers and strings, or they may define more
complex data types.
7. Your application receives the expected values from the proxy function, completely unaware that
they resulted from a Web service call.
To make use of a Web service from an ASP.NET page, your proxy must be created and compiled
appropriately. You can create a proxy to a Web service using either Web Matrix or a command line tool
called
WSDL.exe provided in the .NET Framework SDK. Both of these methods make use of WSDL to
create a proxy, built in the language of your choice. We'll create a new ASP.NET application, which will
access our new ISBN Web service using Web Matrix as it is easier to use.
614
Chapter 16
Creating a Proxy
Building a proxy is a two-step process:
1. Generate the proxy source code automatically.
2. Compile the proxy into a runtime library.
Try It Out Accessing the ISBN Web Service from an ASP.NET Page
In this example, you will build the proxy and a simple page for retrieving book titles from the ISBN Web
service, demonstrating how quickly your Web service applications can be up and running.
1. Open the ISBN.asmx file you just created in Web Matrix.
2. Go to the Tools menu and select Web service Proxy Generator.

3. Fill in the dialog that appears, as shown in Figure 16-12:
Figure 16-12
4. You've created the proxy class in C#, and defined the ISBNService namespace. By selecting a
namespace, you will be able to reference your proxy class from within your consuming
application. The proxy is contained in a file called
ISBNProxy.cs. At the same time, Web
Matrix has also performed the second stage. It has taken the
ISBNProxy.cs and compiled it to
create a DLL file that can be referenced within the code. Once this process is finished, you
should see the dialog shown in Figure 16-13:
Figure 16-13
615
Web Services
5. Now that we have a proxy class and a DLL. We're ready to make use of the ISBN Web service
from within an ASP.NET page. We'll call the
BookInfo.aspx page, and use it to call the Web-
callable function
BookDetail in ISBN.asmx. By using a proxy, the reference to the function's
namespace will appear as if it was a function within the same page. So, create a new ASPX file
called
BookInfo.aspx in Web Matrix, in the folder C:\BegASPNET11\Ch16.
6. Click on the All window and enter the following code:
<%@ Page Language="C#" Debug="true"%>
<%@ Import namespace="ISBNService" %>
<script Language="C#" runat="server">
private void RetrieveBook(object sender, EventArgs e)
{
ISBNService.ISBN ws = new ISBNService.ISBN();
lblBookTitle.Text = ws.BookDetail(txtISBN.Text);
}

</script>
<html>
<body>
<form id="Form1" method="post" runat="server">
Enter an ISBN number to search on:
<br />
<asp:TextBox id="txtISBN" runat="server"></asp:TextBox>
<br />
<asp:Button id="Button1" runat="server" Text="Submit" _
OnClick="RetrieveBook"></asp:Button><br />
<asp:Label id="lblBookTitle" runat="server" Width="152px" _
Height="23px"></asp:Label>
</form>
</body>
</html>
7. Save the file and run it in your browser. You should see the screen shown in Figure 16-14:
Figure 16-14
8. Enter an ISBN that we know is in the Books table, like the ISBN for this book (0764557084) and
you'll see something like Figure 16-15:
616
Chapter 16
Figure 16-15
9. Now try an ISBN that you know will not be found, to ensure that the proxy is actually working;
see Figure 16-16:
Figure 16-16
How It Works
Before Web Matrix came along, we had to use WSDL.exe to generate a proxy class and use a command
line compiler to compile the proxy class, and to create the
ISBNProxy.dll. This is no longer the case as
we can use Web Matrix to perform these two distinct operations in one step. Set the options in the dialog

as follows:

WSDL URL: The location of the Web service.

Namespace: The name by which you can reference the Web service in your ASP.NET code.

Language: The language the proxy class should be generated in.

OutputDirectory: The place where both the proxy class and the assembly should be placed.

SourceFile: The name of the proxy class.

GenerateAssembly: The name of the DLL.
These options ensured that we create the DLL so that it works correctly, and can be added to our
ASP.NET page.
617
Web Services
In our ASP.NET page, we made use of Web Form controls. These controls – <asp:TextBox>,
<asp:Label>, and <asp:Button> – make up the simple form that makes a very specific call to the
BookDetail function.
Upon clicking the
Submit button, the RetrieveBook event fires, as specified in the OnClick attribute of
<asp:Button>:
<asp:Button id="Button1" runat="server" Text="Submit"
OnClick="RetrieveBook" /></asp:Button>
Within the RetrieveBook function, first of all, we create an instance of the proxy class that we'll be
using:
ISBNService.ISBN ws = new ISBNService.ISBN();
Then it's simply a matter of calling the BookDetail function of the ws object. Remember the previous
example where we created the Web method:

[WebMethod]
public string BookDetail(string strIsbn)
{
return GetBookDetails(strIsbn);
}
Here we are actually accessing the same Web Method from our ASPX page. ISBNService.ISBN refers
to our automatically created DLL file, which is used to communicate with the ASMX Web service file
created from the previous example. So once we've created our
ws object using the DLL, we can use all
the Web methods of the object as though they were normal methods.
With a single line of code, we pass the string contents of
txtISBN.text to the Web service and receive
the book title, placing that string into the label
lblBookTitle.text:
lblBookTitle.Text = ws.BookDetail(txtISBN.Text);
Once again, this example proved the simplicity and power of Web services.
Creating a Web Service for the Wrox United
Application
The process for creating a Web service, although relatively easy, can be quite lengthy. So far the examples
have been kept as simple as possible. In fact, the previous example might seem like a long winded way
to go about just returning a single string from our database. The power of Web services lies in the ability
to return more complex items than just single items of data.
We'll now build a Web Method that links back to the Wrox United application and use it to return a set
of results. In fact, the Web service will prompt you for the name of a team, scour the database for the
score from the most recent game, and return that to the user. For the sake of simplicity and compatibility,
618
Chapter 16
we'll still take these results and output them as a single string. However, this string will be created from
a concatenation of both integer and string values that have been gleaned from the database. It is possible
to return this information as a dataset. There isn't a standard way to return a dataset, so by returning our

information as a string, we make it easily consumable to users on all platforms, because a dataset on
Windows can be completely different from a dataset returned by a database on a UNIX server.
Before building the Web method though, we're going to add a results page to the Wrox United
application. This page's functionality is unrelated to Web services, so let's see how it works. We'll borrow
some of the data-reading routines from this page and use this within our Web method to extract a single
result from the database.
Try It Out Adding a Results Page
1. Open up Web Matrix and create a new .aspx page called results.aspx.
2. Next, download the code for results.aspx from – we're not going to
reproduce it here as it is over five pages long!
3. Alter the navbar.ascx navigation bar, so that it points to the new results.aspx page. Amend
the code as follows:

<p>
<asp:HyperLink id="lnkGames" runat="server"
NavigateUrl="Default.aspx">Upcoming Games
</asp:HyperLink>
</p>
<p>
<asp:HyperLink id="lnkResults" runat="server"
NavigateUrl="results.aspx">Results
</asp:HyperLink>
</p>
<p>
<asp:HyperLink id="lnkChat" runat="server"
NavigateUrl="Chat.aspx">Chat
</asp:HyperLink>
</p>

4. Now, open the Wrox United Application and browse to the results.aspx page, as shown in

Figure 1617:
619
Web Services
Figure 16-17
5. You can now see a complete list of the results of the games played. If you click on the column
heading, it will sort the results by the appropriate column.
How It Works
We're only going to look at the data reading routine in the TeamResults function. We create a seemingly
massive variable query string that takes an enormous SQL statement. The SQL statement isn't as scary as
it looks:
string queryString = "SELECT [Games].[Date], [Games].[WroxGoals], " +
"[Games].[OpponentGoals], [Teams].[TeamName], " +
"[Opponents].[OpponentName], [GameTypes].[GameType],"+
"[Games].[GameID] [GameTypes]" +
"FROM [Games], [Teams], [Opponents]," +
"WHERE (([Games].[WroxTeam] = [Teams].[TeamID]) AND " +
"([Games].[OpposingTeam] = [Opponents].[OpponentID])" +
"AND ([Games].[GameType] = [GameTypes].[GameTypeID])" +
"AND ([Games].[Date] < now())) ORDER BY " +
"+ SortExp + SortDir;"
620
Chapter 16
Basically the SQL statement gets the date, opponent goals, team-name, opponent name, type of game
and game identifier from the
Games table. However, as this information is spread across the Games,
Teams, Opponents, and GamesType tables, we have to perform joins to the Games table to extract this
information. If you're not familiar with SQL don't worry, you don't need to be. You just need to
understand that this query (a slightly modified version) will form the heart of our Web service, as this is
exactly the information we need to extract. The only difference is that we want to extract only one result
as opposed to a whole set of results.

The rest of the code in this function just creates a
Command object and supplies the QueryString variable
as the
CommandText. It then runs the ExecuteReader method and returns the dataset as a DataReader
object:
System.Data.IDbCommand dbCommand = new System.Data.OleDb.OleDbCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
dbConnection.Open;
System.Data.IDataReader dataReader =
dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
return dataReader;
}
This is exactly what we'll be doing .
Try It Out Creating The Web Service
1. Open Web Matrix and create a new latestscore.asmx XML Web service with the class name
as
LatestScore and the namespace as WroxUnited.
2. Add the following code into the window making sure that it replaces all of the default code
created by Web Matrix:
<%@ WebService language="c#" class="LatestScore" %>
using System;
using System.Web.Services;
using System.Xml.Serialization;
using System.Data;
using System.Data.OleDb;
using System.Configuration;
//Inherit the WebService class that provides all the built-in features
//that are needed to create a Web Service.
public class LatestScore : System.Web.Services.WebService

{
[WebMethod]
public string ReturnScore(string Team)
{
return GetLatestScore(Team);
}
private string GetLatestScore(string Team)
621
Web Services
{
//Declare the database access objects
OleDbDataReader LibraryDr;
OleDbConnection LibraryConn;
OleDbCommand LibraryCmd;
//Declare the connection string that grants access to the database
string Conn = ConfigurationSettings.AppSettings["ConnectionString"];
//Declare the SQL that will be executed.
string SQL = "SELECT [Games].[WroxGoals], [Games].[OpponentGoals], "+
"[Opponents].[OpponentName], [Games].[Date] "+
"FROM [Games], [Teams], [Opponents] "+
"WHERE (([Games].[WroxTeam] = [Teams].[TeamID]) AND "+
"([Games].[OpposingTeam] = [Opponents].[OpponentID]) AND "+
"([Teams].[TeamName] = \"" + Team + "\")) "+
"ORDER BY [Games].[Date] DESC";
string MaxDate,LatestScore, WroxGoals, OpponentGoals, TeamName,
OpponentName;
//Open the connection to the database.
LibraryConn = new OleDbConnection(Conn);
LibraryCmd = new OleDbCommand(SQL, LibraryConn);
LibraryConn.Open();

LibraryDr = LibraryCmd.ExecuteReader(CommandBehavior.CloseConnection);
if (LibraryDr.Read())
{
MaxDate = Convert.ToString(LibraryDr["Date"]);
WroxGoals = Convert.ToString (LibraryDr["WroxGoals"]);
OpponentGoals = Convert.ToString (LibraryDr["OpponentGoals"]);
OpponentName = Convert.ToString (LibraryDr["OpponentName"]);
LatestScore = MaxDate + " - " + Team + " " + WroxGoals + " " +
OpponentName + " " + OpponentGoals;
}
else
{
//A row was not returned; this book does not exist.
LatestScore = "The team cannot be found in the database";
}
LibraryDr.Close();
return LatestScore;
}
}
3. You can now test the Web service to see if it is working correctly. Go to
http://localhost/latestscore.asmx and browse the LatestScore link that appears, as shown in Figure
16-18. You should be asked for a single parameter – the team. This can be either
The A team or
The B Team:
622
Chapter 16
Figure 16-18
4. When you invoke this service, you should see the result shown in Figure 16-19:
Figure 16-19
5. You'll get the score from the A Team vs Kernel Coders match that was played on the 7th of

September. Go back to the
results.aspx page and sort the columns by date. At the foot of the
screen as shown in Figure 16-20, you'll see that this is indeed the most recent match played:
Figure 16-20
623
Web Services
Go back and check the Web service for the B team and you'll see the result against the Script Kiddies,
which is a 0-1 loss.
How It Works
Our Web service has a single Web Method that calls the GetLatestScore function and supplies it with
a single parameter: the team name:
[WebMethod]
public string ReturnScore(string Team)
{
return GetLatestScore(Team);
}
The GetLatestScore function does all the work. We start by initializing three objects that will return
the data from the database:
OleDbDataReader, OleDbConnection, and OleDbCommand .
OleDbDataReader LibraryDr;
OleDbConnection LibraryConn;
OleDbCommand LibraryCmd;
Next we create a connection string to the database, using the AppSettings from our Web.Config file:
string Conn = ConfigurationSettings.AppSettings["ConnectionString"];
The following line should also be familiar – it's where we create the query string that will be used to
extract our results from the database:
string SQL = "SELECT [Games].[WroxGoals], [Games].[OpponentGoals], "+
"[Opponents].[OpponentName],[Games].[Date] FROM "+
"[Games], [Teams], [Opponents] WHERE (([Games].[WroxTeam] = "+
"[Teams].[TeamID]) AND ([Games].[OpposingTeam] = "

"[Opponents].[OpponentID]) AND ([Teams].[TeamName] = \"" +
"Team + "\")) ORDER BY [Games].[Date] DESC";
What's different here is that we have added a clause that orders the columns returned by the final date.
SQL provides its own parameters for returning maximum values, but in our case, it's easier to "cheat" by
just sorting the data ourselves into the order that we want and then taking the last value only. This
variable contains a query that gets the goals, opponent's goals, opponent team's name, and game date
from the database, so it's a little bit simpler than the one used in
results.aspx.
We create a condition so that only teams that match the team name supplied in the
Team variable are
returned. So if we have supplied the A Team, then it will only return the A team's results. In fact, we
don't even need to return our own team name, as we already have been supplied that by the user, when
they entered the team parameter to the Web service. Once we've created the query, we need to create a
set of variables to store each of the different items of information in. Notice that they are all created as
strings, although they don't have to be; it's just that we want to concatenate the information into one big
string and it's easier to do it this way:
string MaxDate,LatestScore, WroxGoals, OpponentGoals, TeamName, OpponentName;
We open a connection to the database, and supply our SQL query to the Command object and run it
against the database:
624
Chapter 16
LibraryConn = new OleDbConnection(Conn);
LibraryCmd = new OleDbCommand(SQL, LibraryConn);
LibraryConn.Open();
LibraryDr = LibraryCmd.ExecuteReader(CommandBehavior.CloseConnection);
Now we're going to "cheat" to keep the code short. As mentioned in results.aspx, we return a dataset.
Now in the last example, we performed a check for a single row of data. If we're returning a dataset,
then more than a single row is returned. However, to avoid having to create an array of information,
most of it unwanted, we read each row into the variables, and then overwrite each row:
if (LibraryDr.Read())

{
MaxDate = Convert.ToString(LibraryDr["Date"]);
WroxGoals = Convert.ToString (LibraryDr["WroxGoals"]);
OpponentGoals = Convert.ToString (LibraryDr["OpponentGoals"]);
OpponentName = Convert.ToString (LibraryDr["OpponentName"]);
So the first row will read the dates, goals, and name information into our four variables. However, as
pointed out earlier, we sorted the information in the SQL query. We sorted our information in ascending
order, by date, and restricted it to the results of only one team. Thus, we know that the last line of
information in the dataset must be the most recent line. Plenty of information is read into the variables,
but it is overwritten. Only the most recent set of information is kept. As data readers move through
datasets sequentially, and we have already sorted the dataset into ordered data, we know that only
information from the last row – the one with the most recent date – is stored.
We concatenate this into the
LatestScore variable:
LatestScore = MaxDate + " - " + Team + " " + WroxGoals + " " + OpponentName +
" " + OpponentGoals;
We perform a check to make sure that the DataReader isn't empty. It would be empty only if someone
supplied a team name that wasn't found in the database. Just in case this is true, the
LatestScore
variable is supplied with an appropriate message instead:
else
{
//A row was not returned; this book does not exist.
LatestScore = "The team cannot be found in the database";
}
Lastly, we close the DataReader and return the contents of the function to the Web method:
LibraryDr.Close();
return LatestScore;
It's now a straightforward task to create a proxy client using the same method from our ISBN example
and change the class so that it queries the

ReturnScore Web method instead. We're not going to supply
the code here to do that, but instead leave that as an exercise for the reader to complete, as the code
changes needed are minimal and WebMatrix can do most of the work for you.
We have a Web service that takes a team name and returns as a string, the date of the latest game played
by the team and the score for that team. That information is widely available to be used in anybody's
application now, and not just ours. But how would someone else go about discovering this information
so as to be able to use it?
625
Web Services
Web Service Discovery
As you begin to build Web Service-integrated applications, it will become increasingly important to
locate services that provide the functions you need, or alternatively post your own Web services so that
others can make use of them. Universal Description, Discovery, and Integration (UDDI)
is a Microsoft
backed initiative that allows you to do this.
Whenever an industry initiative gains the support of several major players, it will usually become
mainstream. For this reason, UDDI is positioned to dominate the Web service discovery field in the
future. The UDDI service (accessible from
or />lets businesses register themselves and list their existing Web services at no charge. Anyone can browse
and search the UDDI database for a service that may suit their needs. UDDI provides information such
as contact details (address, phone number, e-mail address, Web site), business details (DUNS number
and industry sector), and a discovery URL for each service. WSDL is a key component of the UDDI
project.
By using
you can search for businesses that provide Web services, select the
WSDL appropriately, and build your proxies (see Figure 16-21):
Figure 16-21
626
Chapter 16

×