Building the ProductService Web Service


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:

    [WebService(Namespace="http://www.contentmaster.com/NorthwindServices")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Service : System.Web.Services.WebService {     … }

  5. 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.

  6. 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:

    <connectionStrings>     <add name="NorthwindConnectionString"        connectionString="Data Source=YourServer\SQLExpress; Initial Catalog=Northwind; Integrated Security=True"          providerName="System.Data.SqlClient"/> </connectionStrings>

  7. 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:

    [WebMethod] public decimal HowMuchWillItCost(string productName, int howMany) { }

    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.

  8. Add the following using statements to the top of the file:

    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.

  9. 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.

  10. 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.

  11. Add the following method to the Service class. Notice that this is a local, private method and is not tagged with the [WebMethod] attribute:

    private void handleWebException(Exception e) {     EventLog log = new EventLog("Application");     log.Source = "NorthwindServices";     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.

  12. 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:

    catch(Exception e) {     handleWebException(e);     throw new Exception(); }    

  13. 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"  %>

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 http://tempuri.org. 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.

    graphic

    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.

    graphic

  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.

    graphic

  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="http://www.w3.org/2001/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns="http://www.contentmaster.com/NorthwindServices">   <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:

    private int productID; private string productName; private int supplierID; private int categoryID; private string quantityPerUnit; private decimal unitPrice; private short unitsInStock; private short unitsOnOrder; private short reorderLevel; private bool discontinued;

  4. Create a read/write property called ProductID. The property provides access to the private productID variable:

    public int ProductID {     get { return this.productID; }     set { this.productID = value; } }

  5. 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:

    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:

    [WebMethod] public Product GetProductInfo(string productName) { }

  2. Add the following statements to the GetProductInfo method (replace YourServer when calling the SqlConnection constructor with the name of your SQL Server computer):

    Product product = new Product(); SqlConnection sqlConn = null; try {     ConnectionStringSettings cs =        ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];     string connString = cs.ConnectionString;     sqlConn = new SqlConnection(connString);     SqlCommand sqlCmd = new SqlCommand();     sqlCmd.CommandText = "SELECT * FROM Products " +         "WHERE ProductName = '" + productName + "'";     sqlCmd.Connection = sqlConn;     sqlConn.Open();     SqlDataReader productData = sqlCmd.ExecuteReader();     if (productData.Read())     {         product.ProductID = productData.GetInt32(0);         product.ProductName = productData.GetString(1);         product.SupplierID = productData.GetInt32(2);         product.CategoryID = productData.GetInt32(3);         product.QuantityPerUnit = productData.GetString(4);         product.UnitPrice = productData.GetDecimal(5);         product.UnitsInStock = productData.GetInt16(6);         product.UnitsOnOrder = productData.GetInt16(7);         product.ReorderLevel = productData.GetInt16(8);         product.Discontinued = productData.GetBoolean(9);     }     else     {         throw new ArgumentException("No such product " + productName);     }     productData.Close();     return product; } catch(ArgumentException e) {     handleWebException(e);     throw e; } catch(Exception e) {     handleWebException(e);     throw new Exception(); } finally {     if (sqlConn != null)         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.

  3. 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.

  4. 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.

    graphic

  5. Close the Internet Explorer windows.




Microsoft Visual C# 2005 Step by Step
Microsoft® Visual C#® 2005 Step by Step (Step By Step (Microsoft))
ISBN: B002CKYPPM
EAN: N/A
Year: 2005
Pages: 183
Authors: John Sharp

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