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

Tài liệu Building the ProductService Web Service 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 (34 KB, 13 trang )



Building the ProductService Web Service
In this chapter, you will create the ProductService Web service. This Web service will
expose two Web methods. The first method will allow the user to calculate the cost of
buying a specified quantity of a particular product in the Northwind Traders database,
and the second method will take the name of a product and return all the details for that
product.
Creating the ProductService Web Service
In the first exercise, you will create the ProductService Web service and implement the
HowMuchWillItCost Web method. You will then test the Web method to ensure that it
works as expected.
Create the Web service
1. In the Microsoft Visual Studio 2005 programming environment, create a new Web
site using the ASP.NET Web Service template. Make sure you specify File System
as the Location, and Visual C# for the Language. Create the Web site in the
\Microsoft Press\Visual CSharp Step by Step\Chapter 28\NorthwindServices
folder in your My Documents folder.
IMPORTANT
Ensure you select the ASP.NET Web Service template and not the ASP.NET Web
Site template.
Visual Studio 2005 generates a Web site containing folders called App_Code and
App_Data, and a file called Service.asmx. The .asmx file contains the Web service
definition. The code for the Web service is defined in the Service class, stored in
the file Service.cs in the App_Code folder, and displayed in the Code and Text
Editor window.
2. In the Solution Explorer, click the NorthwindServices project. In the Properties
window, set the Use dynamic ports property to False, and set the Port number
property to 4500.
By default, the Development Web server provided with Visual Studio 2005 picks
a port at random to reduce the chances of clashing with any other ports used by


other network services running on your computer. This feature is useful if you are
building and testing ASP.NET Web sites in a development prior to copying them
to a production server such as IIS. However, when building a Web service it is
more useful to use a fixed port number as client applications need to be able to
connect to it.
3. In the Service.cs file in the Code and Text Editor window, examine the Service
class; it is descended from System.Web.Services.WebService. Scroll to the bottom
of the class. A sample Web service method called HelloWorld is provided by the
Visual Studio 2005 template. This method simply returns the string, “Hello
World.” Notice that all methods that a client can call must be tagged with the
[WebMethod] attribute. Comment out the HelloWorld method and the
[WebMethod] attribute; you will not need this method in this exercise.
4. Above the Service class you can see two more attributes: [WebService] and
[WebServiceBinding]. The [WebServiceBinding] attribute identifies the level of
the Web services interoperability specification that the Web service conforms to.
You can ignore this attribute in this chapter and leave it set to its default value.
The [WebService] attribute indicates the namespace used to identify the Web
service. Change the value of this attribute, as shown below:
5. [WebService(Namespace="
6. [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
7. public class Service : System.Web.Services.WebService
8. {
9. …
}
10. Right-click the NorthwindServices project in the Solution Explorer, and then click
Add New Item. In the Add New Item dialog box, click the Web Configuration File
template. Make sure the name of the file is set to Web.config and then click Add.
This action adds a Web configuration file to your project, containing default
settings. The Web.config file appears in the Code and Text Editor window.
11. Modify the <connectionStrings/> element in the Web.config file. Add the

following <add> sub element that defines a new connection string that you will
use for connecting to the Northwind database. Replace YourServer with the name
of your computer. Make sure you add a </connectionStrings> element:
12. <connectionStrings>
13. <add name="NorthwindConnectionString"
14. connectionString="Data Source=YourServer\SQLExpress; Initial
Catalog=Northwind;
15. Integrated Security=True"
16. providerName="System.Data.SqlClient"/>
</connectionStrings>
17. Display the Service.cs file in the Code and Text Editor window. Add the following
method to the Service class, underneath the commented-out HelloWorld method:
18. [WebMethod]
19. public decimal HowMuchWillItCost(string productName, int howMany)
20. {
}
This method expects the client to pass in the name of a product found in the
Products table in the Northwind Traders database, and a quantity of that product.
The method will use the information in the database to calculate the cost of
supplying this quantity of the product and pass this cost back as the return value of
the method.
21. Add the following using statements to the top of the file:
22. using System.Configuration;
using System.Data.SqlClient;
This System.Configuration namespace contains classes that you can use for
reading configuration settings from the Web.config file. The
System.Data.SqlClient namespace contains the Microsoft ADO.NET classes for
accessing Microsoft SQL Server.
23. In the HowMuchWillItCost Web method, type the following statements:
SqlConnection sqlConn = null;

try
{
ConnectionStringSettings cs =
ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];
string connString = cs.ConnectionString;
sqlConn = new SqlConnection(connString);
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.CommandText = "SELECT UnitPrice FROM Products " +
"WHERE ProductName = '" + productName + "'";
sqlCmd.Connection = sqlConn;
sqlConn.Open();
decimal price = (decimal)sqlCmd.ExecuteScalar();
return price * howMany;
}
catch(Exception e)
{
// Handle the exception
}
finally
{
if (sqlConn != null)
sqlConn.Close();
}
This code the connection string called NorthwindConnectionString from the
Web.config file by using the static ConnectionStrings property of the
ConfigurationManager class. This property returns the corresponding entry from
the Web.config file. The entry contains information such as the name of the
connection string, the provider, and the connection string itself. You can extract
the connection string from this entry by using the ConnectionString property.
The code then connects to the Northwind Traders database and runs a SQL

SELECT statement to retrieve the UnitPrice column for the selected product in the
Products table. The ExecuteScalar method is the most efficient way of running a
SELECT statement that returns a single value. The UnitPrice column is stored in
the price variable, which is then multiplied by the howMany parameter that is
passed in to calculate the cost.
NOTE
Refer back to the section “Using ADO.NET Programmatically” in Chapter 23,
“Using a Database,” if you need to refresh your memory on using ADO.NET to
access a SQL Server database.
The Web service uses a try catch block to trap any runtime errors, although it
does not validate the parameters passed in (for example, the client might supply a
negative value for the howMany parameter). You can add the necessary code to
validate the parameters yourself.
It is important to make sure you close the connection to the database, even after an
exception. That is the purpose of the finally block.
If an exception occurs, you should capture the details somewhere. It is usually not
advisable to send the details of the exception back to the user as it might not mean
much to them, and there can also be security concerns if too much information
about the internal workings of your Web service is reported back to the user
(details such as the names and addresses of databases are just the sort of things
that hackers trying to break into your system will find useful). However, the
administrator for the Web site should be very interested in this information. The
ideal place to capture details of Web service exceptions is the Windows Event
Log.
24. Add the following using statement to the top of the file:
using System.Diagnostics;
This is the namespace that contains the classes for interacting with the event logs.
25. Add the following method to the Service class. Notice that this is a local, private
method and is not tagged with the [WebMethod] attribute:
26. private void handleWebException(Exception e)

27. {
28. EventLog log = new EventLog("Application");
29. log.Source = "NorthwindServices";
30. log.WriteEntry(e.Message, EventLogEntryType.Error);
}
These statements add an entry to the Windows Application event log containing
the details of the exception passed in as the parameter, flagging it as an error (you
can also store warnings and informational messages in the event log).
However, writing to any Windows event log is a privileged operation, and requires
that the Administrator for your computer has granted you the appropriate access
rights. You will see how to grant these rights in the next exercise.
31. In the catch block for the HowMuchWillItCost Web method, remove the comment
and type the following statements. The first statement calls the
handleWebException method, passing it the exception that occurred as its
parameter. The throw statement throws an empty exception, which simply causes
an “HTTP 500 – Internal server error” page to be displayed in the user's browser:
32. catch(Exception e)
33. {
34. handleWebException(e);
35. throw new Exception();
36. }

37. On the Build menu, click Build Web Site to compile the Web service.
The Structure of a Web Service
Web services in the .NET Framework are similar to ASP.NET Web applications
inasmuch as they consist of two parts: the .asmx file which defines the Web service, and
a C# code file with the suffix .cs. The.cs file actually contains the C# code for the
methods that you add to the Web service, and is the file displayed in the Code and Text
Editor window by default. This file is located in the App_Code folder of the Web site.
You can see this file in the Solution Explorer if you expand the App_Code folder.

You can view the .asmx file by double-clicking the file in the Solution Explorer. The
.asmx for the NorthwindServices Web service looks like this:
<%@ WebService Language="C#" CodeBehind="~/App_Code/Service.cs"
Class="Service" %>
It is highly unlikely that you will need to modify this file.
Web Service Namespaces
A Web service should use a unique namespace to identify itself so that client applications
can distinguish it from other services on the Web. By default, Web services created with
Visual Studio 2005 use . This is fine for Web services that are under
development, but you should create your own namespace when you publish the Web
service. A common convention is to use your company's URL as part of the namespace,
together with some other form of identifier. You should note that the namespace does not
have to be an existing URL—its purpose is only to uniquely identify the Web service.
Grant Access Rights to use the Windows Event Log
IMPORTANT
You will require the password for the Administrator user on your computer to
perform the tasks in this exercise.
1. On the Windows Start menu, point to All Programs, point to Accessories, and then
right-click Command Prompt. Click Run As. In the Run As dialog box, click “The
following user.” Type Administrator for the User name, type the password, and
then click OK.
A Command Prompt window opens. This window is running as Administrator,
and you can do anything an administrator can do—be careful.
2. In the Command Prompt window, type regedit.
The Registry Editor starts. This tool allows you to configure your computer. Be
very careful as you can easily cause serious problems that require you to reinstall
your operating system.
3. In the tree view in the left-hand pane, expand My
Computer\HKEY_LOCAL_MACHINE\
SYSTEMCurrentControlSet\Services\Eventlog.

4. Right-click the Application node, point to New, and then click Key. A new key
called New Key #1 appears in the left-hand pane. Overwrite this name with the
text NorthwindServices.
TIP
If you mistype the name, you can change it by right-clicking the key, and then
clicking Rename. Do not rename any other keys.
5. Close the Registry Editor, and then close the Command Prompt window.
Test the Web method
1. Return to Visual Studio 2005. In the Solution Explorer, right-click Service.asmx
and then click View in Browser.
The ASP.NET Development Web Server starts and displays a message indicating
that the Web service is available at the address
http://localhost:4500/NorthwindServices .
Internet Explorer starts and moves to the URL
http://localhost:4500/NorthwindServices/Service.asmx, displaying the Service test
page.

The test page allows you to view the WSDL description by clicking the Service
Description hyperlink or to test individual Web methods (in this example, there is
only one: HowMuchWillItCost).
2. Click the Service Description hyperlink.
The URL changes to
http://localhost:4500/NorthwindServices/Service.asmx?WSDL, and Internet
Explorer displays the WSDL description of your Web service.
3. Click the Back button in the Internet Explorer Toolbar to return to the test page.
Click the HowMuchWillItCost hyperlink.
Internet Explorer generates another page that allows you to specify values for the
parameters and test the HowMuchWillItCost method. The page also displays
sample SOAP requests and responses.


4. In the productName text box, type Aniseed Syrup, and then type 23 in the
howMany text box. Click Invoke.
The Web method runs, and a second Internet Explorer window opens and displays
the response in SOAP format.

5. Close both Internet Explorer windows and return to the Visual Studio 2005
programming environment.
Handling Complex Data
SOAP enables you to pass complex data structures between a client and a Web service as
input parameters, output parameters, or return values. To do this, the data structures
themselves are converted into a format that can be tranmitted over the network and
reassembled at the other end. This process is known as serialization. For example, in the
following exercise, you will build a class that holds information about a product in the
Northwind Traders database. The class will contain many properties, including
ProductID, ProductName, SupplierID, and CategoryID. You will then create a Web
method that returns an instance of this class. The SOAP serialization process will convert
the class into XML, send the serialized version across the network using SOAP, and
reconstruct the class from the XML at the other end (a very simple marshalling
technique). The following structure shows an example of how the class will be serialized
for transmission:
<?xml version="1.0" encoding="utf-8" ?>
<Product xmlns:xsi="
xmlns:xsd="
xmlns="
<ProductID>1</ProductID>
<ProductName>Chai</ProductName>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18.0000</UnitPrice>

<UnitsInStock>39</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>false</Discontinued>
</Product>
The serialization process is automatic and largely transparent as long as you follow a few
simple rules when defining the class. In particular, serialization can only be used when
transmitting classes that contain public fields and properties. If an object contains private
members that do not have corresponding get and set property accessors, it will not
transfer correctly; the private data will not be initialized at the receiving end. Also note
that all properties must have both get and set accessors. This is because the XML
serialization process must be able to write this data back to the object after it has been
transferred. Additionally, the class must have a default (with no parameters) constructor.
It is common to design classes used for SOAP purely as containers for transmitting data.
You can then define additional functional classes that act as facades providing the
business logic for these data structures. Users and applications would gain access to the
data by using these business facades.
If you want, you can customize the serialization mechanism using the various SOAP
attribute classes of the System.Xml.Serialization namespace or define your own XML
serialization mechanism by implementing the ISerializable interface of the
System.Runtime.Serialization namespace.
Define the Product class
1. In the Solution Explorer, right-click the App_Code folder, and then click Add
New Item. In the Add New Item dialog box, click the Class template, and then
type Product.cs for the name of the new class. Click Add to create the class.
NOTE
It is conventional to place source code files for an ASP.NET Web site in the
App_Code folder.
2. In the Solution Explorer, expand the App_Code folder if it is not already
expanded. Double-click the file Product.cs to display it in the Code and Text

Editor window.
3. Add the following private variables to the Product class, above the constructor.
There is one variable for each of the columns in the Products table in the database:
4. private int productID;
5. private string productName;
6. private int supplierID;
7. private int categoryID;
8. private string quantityPerUnit;
9. private decimal unitPrice;
10. private short unitsInStock;
11. private short unitsOnOrder;
12. private short reorderLevel;
private bool discontinued;
13. Create a read/write property called ProductID. The property provides access to the
private productID variable:
14. public int ProductID
15. {
16. get { return this.productID; }
17. set { this.productID = value; }
}
18. Add properties to provide read/write access to the remaining variables. You can
achieve this task in at least two different ways: you can manually write each get
and set accessor, or you can get Visual Studio 2005 to generate them for you.
To use Visual Studio 2005 to generate a property for the productName variable,
double click the productName variable in the code to highlight it. On the Refactor
menu, click Encapsulate Field. In the Encapsulate Field dialog box, ensure the
Property name is set to ProductName, clear the “Preview reference changes”
check box, and then click OK, as shown in the following graphic:

The following property is added to the Product class:

public int ProductName
{
get { return productName; }
set { productName = value; }
}
Repeat this process for the remaining fields.
Properties and Field Names: A Warning
Although it is a commonly accepted practice to give properties and private fields the
same name that differ only in the case of the initial letter, you should be aware of one
drawback. Examine this code:
public int CategoryID
{
get { return this.CategoryID; }
set { this.CategoryID = value; }
}
This code will compile perfectly well, but it results in the program hanging whenever the
CategoryID property is accessed. This is because the get and set accessors reference the
property (upper-case C) rather than the private field (lower-case c), which causes an
endless recursive loop. This sort of bug is very difficult to spot!
Create the GetProductInfo Web method
1. Return to the Service.cs file in the Code and Text Editor window. Add a second
Web method called GetProductInfo that takes a product name (a string) as its
parameter and returns a Product object:
2. [WebMethod]
3. public Product GetProductInfo(string productName)
4. {
}
5. Add the following statements to the GetProductInfo method (replace YourServer
when calling the SqlConnection constructor with the name of your SQL Server
computer):

6. Product product = new Product();
7. SqlConnection sqlConn = null;
8. try
9. {
10. ConnectionStringSettings cs =
11. ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];
12. string connString = cs.ConnectionString;
13. sqlConn = new SqlConnection(connString);
14. SqlCommand sqlCmd = new SqlCommand();
15. sqlCmd.CommandText = "SELECT * FROM Products " +
16. "WHERE ProductName = '" + productName + "'";
17. sqlCmd.Connection = sqlConn;
18. sqlConn.Open();
19. SqlDataReader productData = sqlCmd.ExecuteReader();
20. if (productData.Read())
21. {
22. product.ProductID = productData.GetInt32(0);
23. product.ProductName = productData.GetString(1);
24. product.SupplierID = productData.GetInt32(2);
25.
product.CategoryID = productData.GetInt32(3);
26. product.QuantityPerUnit = productData.GetString(4);
27. product.UnitPrice = productData.GetDecimal(5);
28. product.UnitsInStock = productData.GetInt16(6);
29. product.UnitsOnOrder = productData.GetInt16(7);
30. product.ReorderLevel = productData.GetInt16(8);
31. product.Discontinued = productData.GetBoolean(9);
32. }
33. else
34. {

35. throw new ArgumentException("No such product " + productName);
36. }
37.
38. productData.Close();
39. return product;
40. }
41.
42. catch(ArgumentException e)
43. {
44. handleWebException(e);
45. throw e;
46. }
47.
48. catch(Exception e)
49. {
50. handleWebException(e);
51. throw new Exception();
52. }
53.
54. finally
55. {
56. if (sqlConn != null)
57. sqlConn.Close();
}
These statements use ADO.NET to connect to the Northwind Traders database and
retrieve the details for the specified product from the database.
This method uses the same approach as the HowMuchWillItCost method to handle
most exceptions. However, if no product with the specified product name exists,
this information is useful to the client and probably does not compromise security.
In this situation, the method throws an ArgumentException. To prevent this

exception being filtered out by the generic exception handler, the
ArgumentException handler re-throws the same exception, passing its details back
to the client.
58. Build the Web Site. Run the Web service by right-clicking Service.asmx in the
Solution Explorer and clicking View in Browser. When Internet Explorer displays
the test page, click the GetProductInfo hyperlink.
The GetProductInfo test page appears, enabling you to test the GetProductInfo
method.
59. In the productName text box, type Aniseed Syrup and then click Invoke.
The Web method runs, fetches the details for Aniseed Syrup, and returns a Product
object. The Product object is serialized as XML and displayed in Internet
Explorer.

60. Close the Internet Explorer windows.



×