2002-09-13 11:38
使用Visual Studio.Net建立web service
ARTICLE
A Web Service is programmable logic that provides data and services to other applications by exposing its features over the Internet or Intranet using Standard Internet protocols like HTTP, XML and SOAP. The Simple Object Access Protocol (SOAP) defines a lightweight protocol for information exchange.exchange and the SOAP specification can be found at SOAP Specification Version 1.1. It is fast becoming the universally accepted Internet standard for data exchange with the input from companies like Microsoft, IBM and Developmentor. It is used for many things from simple data exchange to RPC like functionality.
Since web services are exposed using Internet standards like HTTP, XML and SOAP, any application that can communicate using these standards can interact with a web service.
For more information on an introduction to web services,services please take a look at the article Web Services with ASP.NET on MSDN.
Handling of Web Services
Web services are saved with the extension .asmx. Whenever the ASP.NET runtime receives a request for an .asmx file, it redirects the request to a handler called WebServicesHandlerFactory. A Handler is a class that receives the requests from the ASP.NET runtime and services the needs of the client by executing appropriate code.
This mapping is established in the global config.web (this is similar to the IIS Metabase) file that can be found in C:\WINDOWS\Microsoft.NET\Framework\v1.0.2204 when you install Visual Studio.Net Beta 1. This file is a global file that holds configuration settings for the entire ASP.NET and this can be overridden for the individual applications by modifying the config.web file that can be found in the root directory of that ASP.NET application.
In the config.web file, for the handling of web services, it is specified as:
<add verb="*"
path="*.asmx"
type="System.Web.Services.Protocols.WebServiceHandlerFactory,System.Web.Services"
validate="false"/>
This indicates whenever any client makes a request for an .asmx file, ASP.NET runtime creates an instance of the WebServiceHandlerFactory class (that resides in the System.Web.Services namespace) and hands the request over to it. The attribute verb=* shows that all HTTP requests like GET, POST, PUT for an .asmx file should be handled by the WebServicesHandler class.
Role of SDL in exposing Web Services
Web services provide Well-defined interface through an XML based language called Services Description Language (SDL). This language describes the functionality of the web service and describes each of the methods, the parameters they take, and what they return. It is similar to the way a COM object exposes its functionalities through functionality through a Type Library.
Since SDL is an XML based grammar, any application in the Internet can query the web services for its contract and invoke methods of it without worrying about the language or the platform in which it is implemented. Microsoft has already announced that SDL is going to be renamed as WSDL (Web Services Description Langauge) with the new specifications implemented in the Language) and will be based on open Internet standards like XML and SOAP, in the Beta 2 of the Visual Studio.Net. More information on this can be found in this link: W3C Acknowledges Web Services Description Language (WSDL) 1.1.
A Fictitious E-Commerce Scenario About UDDI
UDDI is a specification that defines a way to publish and discover information about web services in the Internet. When a business wants to expose its web services to its partners, it can do that using a common web services description file in XML format that is written using UDDI specification. This XML file is called Business Registry and it gives information about the business entity and the web services it has. Once the business registers information about its web services in the business registry using the UDDI Programmers API specification, it is automatically replicated to all the other UDDI nodes using UDDI Cloud Services. Once this is done, the information about web services is freely available to anyone who needs to know what services are exposed by a given business.
The information available in the Business Registry can be classified into 3 parts: white pages that includes address, contact, and known identifiers; yellow pages that identifiy the industry category that the business falls in and green pages that gives technical information about the services exposed by the business. Developers can use the green pages in the Business Registry to locate information about the technical details of web services, and the yellow pages of the Business Registry to find companies in a given industry with a given type of service.
UDDI uses standards-based technologies such as HTTP, XML and SOAP to create a uniform service description format and service discovery protocol.
More information about UDDI can be found in the official UDDI website.
Store Locator Application
In the first part of the article, we will see how an Fictitious Web portal called www.storelocator.com has created a Store Locator application that uses Web services to increase its Business opportunities by providing data to its customers using rich Web services. This Website has an E-Commerce engine that collects data from their partners and expose that data to their consumers in a meaningful manner. This web service has a method called GetStores that provides information about the list of stores that are available in an area based on the zip code and the name of the store. It also has a web method called GetProducts that returns the list of products available in a store based on the StoreID of the store.
For this application we will create a simple application with two tables. A Store table that stores a list of the stores throughout the country and a Product table that holds information about all the products available in all the stores.
To create this Web service, we create a new Visual C# web service project and add these methods. Call it ProdStoreWebService. Then we will add the following methods to the web service.
GetStores that provides a list of stores in a particular area
based on the name of the Store and the Zip Code.
GetProducts that gives the
list of products available in a particular Store based on the
StoreID.
ADO.NET
The above methods use ADO.NET to access the SQL Server 2000 Database. ADO.NET comes with two kinds of Managed Providers called ADO Managed Providers and SQL Managed Providers.
ADO Managed Providers use the native OLEDB library to communicate with heterogeneous data sources. SQL Managed Providers use a protocol called tabular data stream to access the SQL Server and it is optimized to provide high performance data access. In this article, we use SQL Managed Providers for all the data access. For more information on ADO.NET, please take a look at this article ADO.NET for the ADO Programmer from MSDN.
Implementation of the GetStores Method
One of the methods exposed by the ProdStore web service is a GetStores method that takes a ZipCode and a StoreName and returns the list of Stores in that area.
The attribute [WebMethod] indicates that the method is to be exposed as a web service and when it is deployed, Visual Studio.NET runtime provides all the plumbing required to make this method discoverableavailable to the clients who wish to use this web service.
[WebMethod]
public DataSet GetStores(String ZipCode,String
StoreNameLike)
{
SQLConnection connSQL;
SQLDataSetCommand
cmdSQL;
DataSet dstStores;
String strConn;
String
strCommand;
dstStores = new DataSet();
strConn =
"server=localhost;uid=sa;pwd=;database=home";
strCommand = "Select * from
Store Where StoreName Like '%" + StoreNameLike + & _
"%' and Zip='" +
ZipCode + "'";
Here we create a SQLConnection object passing the ConnectionString as the argument.
connSQL = new SQLConnection(strConn);
The following lines are enclosed within a try..catch block to catch any errors that may occur during the execution of those statements.
try
{
In this line an instance of SQLDataSetCommand object is created and supplied with SQL Command to be executed and the SQLConnection object SQLDataSetCommand acts as the link between a table within a dataset and a table in a server.
cmdSQL = new SQLDataSetCommand(strCommand,connSQL);
To populate the Dataset with the results from the query, the FillDataSet method of the SQLDataSetCommand object is called.
cmdSQL.FillDataSet(dstStores,"Store");
}
Throw the errors back to the client whenever an Exception occurs. You can have multiple catch statements to catch different kinds of Exceptions with the code to catch the most inclusive Exception (to avoid unreachable code compiler error) class in the last catch block.
catch(Exception ex)
{
throw
ex;
}
The statements in finally are always executed regardless of whether the statements in the try block are executed successfully or not. Here we inspect the state property of the SQLConnection to check whether the connection is open. If the Connection is open, it is closed.
finally
{
if ( connSQL.State == DBObjectState.Open
)
connSQL.Close();
}
Before we exit from the function, the Dataset is returned as the return value of the function.
return dstStores;
I will briefly explain the advantage of returning a Dataset from a web service in the latter section of this article.
Implementation of GetProducts Method
The GetProducts method takes StoreId as an argument and returns the list of products in that store. It is similar to the GetStores method except for the change in the query. The code is available for download along with the support material of this article.
Advantages of Characteristics of Datasets
Let us go over some of the characteristics of Datasets and see how they make a perfect fit in the disconnected Internet environment.
Datasets can be considered as a Memory Resident disconnected
copy of the database with all the relationships between the tables established.
Datasets can be Typed or UnTyped. You can create a Strongly typed
Dataset by associating an .xsd schema file with it. The schema contains the
table names, column names, the data types of the columns and information about
constraints.
Typed Datasets allow Visual Studio.Net to provide
Intellisense feature and allows tables and columns to be accessed as if they are
objects. It also makes programming less error-prone.
Since Datasets use
XML based marshalling, any application written in any platform that is capable
of understanding XML can process the dataset that is created using ADO.NET.
Components can exchange datasets without worrying about the details of
the application that has created the dataset. For example, a business object in
the middle tier might create and populate a dataset, then send it to another
component elsewhere in the application for processing.
Creating clients that consume web services
Now that we have created the web service, we will see how to create an ASP.NET web client that invokes the methods of the web services. The important step in consuming web services is creating a Proxy class that mediates between the client and the actual web service that might be present anywhere in the Internet.
There are two different approaches to creating a proxy class.
Using the WebServiceUtil tool that is supplied with Microsoft.NET
framework SDK and the advantage of this approach is that you do not require a
copy of Visual Studio.NET.
Using the Add Web Reference option and
letting Visual Studio.NET create the proxy class for us.
We will see both
these approaches.
Creating the Proxy class using WebServiceUtil
WebServiceUtil is a Console Application that is supplied with the Microsoft.NET Platform SDK. The proxy created by Visual Studio.NET can only work with SOAP. If you want to create a proxy that has to make HTTP Requests like GET and POST to access the web service, then WebServiceUtil comes in handy for you.has to be used.
This utility makes a request for the SDL of the remote web service via HTTP and generates a proxy class. To create a proxy for our web service in C#, the following command can be used.
WebServiceUtil /c:proxy
/language:Csharp
/pa:http://localhost/ProdStoreWebService/ProdStoreWebService.asmx?sdl
/out:ProdStoreWebServiceProxy.cs
The switch /c:proxy is used to indicate that a proxy class needs
to be created.
/pa: - Indicates the path of the web service for which
the proxy class needs to be created.
/out: - Specifies the path and the
name of the output proxy class file to be created.
/language:Csharp:
-Specifies that a C# proxy class has to be created.
By default,
WebServiceUtil generates a C# proxy class. To override the default setting, the
language can be specified.
The proxy created by the WebServiceUtil looks
like this:
Once the proxy class is created, open a new Visual C# Project with the Class library as the project template and add the generated proxy class to the project ensuring that the class is enclosed in a namespace. Add a reference to System.XML, System.XML.Serialization, System.Web,System.Web and System.Web.Services to serialize the requests to the web service into XML format. Since the web and service returns a Dataset to the client, a reference to the System.Data namespace is also added. Then build the project by choosing Build from the Build menu.
Creating an ASP.NET client for the WebService
Now we have created a proxy class library that can be used by the client to make calls to the actual web service. The proxy receives the requests from the client, packages the parameters in a SOAP Request using XML standards and marshals it across the Internet or Intranet to the web service. The web service receives the request from the proxy, executes the methods and sends the results back to the proxy in the form of a SOAP Response. Finally the proxy unpacks the response and passes the results to the client.
We will create an ASP.NET Web form client to access the Web Service. Open a new Visual C# Web Application and add a reference to the proxy class library which was created in the previous step. And also add a reference to the System.Web.Services namespace. Import the Proxy class library's namespace by adding the line of code:
Using ProdStoreWebServiceProxy;
String zip;
String storeName;using
ProdStoreWebServiceProxy;
This code resides in the file called webFormStoreLocator.cs that is linked to the webFormStoreLocator.aspx file using the codebehind attribute.
The ASP.NET web form client that invokes the web services methods looks like this.
String Zip;
String StoreName;
DataSet dstStore=
new DataSet();
zipZip = txtZip.Text;
storeName =
txtStoreName.Text; StoreName = txtStoreName.Text;
This line creates an instance of the proxy class that acts as a skeleton of the Web service.
WebService1 objService = new WebService1();
The GetStores method is invoked and is passed the zipzipcode and store name as the arguments and returns a DataSet.
dstStore = objService.GetStores(zip,storeName);objService.GetStores(Zip,StoreName);
The Data View obtained from the Dataset is bound to the Data Grid Control by assigning the custom default view of the Store table to the DataGrid control gridStores.
gridStores.DataSource =
dstStore.Tables["Store"].DefaultView;
gridStores.DataBind();
gridStores.Visible
= true;
The output from the page looks like this:
Creating a proxy class through Visual Studio.NET
Visual Studio.NET has a feature called Add Web Reference that automates all the plumbing required for creating the proxy class. When you add a web reference of the web service to your project, Visual Studio.NET automatically generates a proxy class that interfaces with the web and it also provides a local representation of the web service.
To add the web reference, right click on the project in the Solution explorer and select Add Web Reference. In the Add Web Reference dialog box, enter the path of the disco file of the web service. The disco file is an XML data file that is used for controlling the dynamic discovery of the web service for the clients by obtaining SDLs and Schemas together.
On the left side of the dialog box, it shows the discovery document and by clicking View Contract you can view the Service Description Language (SDL) that exposes the Contract of the web service. When you Click Add Reference, Visual Studio.NET creates a folder with the name of the server name under the Web References folder. By default, the name of the server is considered as the namespace for the local Proxy class. To assign a different namespace for the proxy, rename the server name folder with the new name. Now, import the proxy namespace into the current project. Once we have the proxy namespace imported into the current application, the Web Callable methods (methods that are decorated with the keyword Web Method) can be invoked transparently as explained in the previous step.
Creating a Transactional Web service
In the remaining part of the article, we will see how to create a transactional web service that uses the COM Components developed in Visual Basic 6 and hosted in Component Services in Windows 2000, to provide functionality to the consumers of the web services.
There are many cases when it becomes neither practical nor necessary to upgrade a COM component simply to incorporate its features into a .NET application. It often makes more sense to access existing functionality through the excellent interoperation services provided by the runtime. This allows companies to leverage the existing investments in the existing middle tier objects written to run under MTS or COM+.
Middle tier COM objects
In this section, we will create two VB6 COM components called Store_DB and Product_DB, with Store_DB having the clsStore class and Product_DB having clsProduct. Both these classes are marked with the MTSTransactionMode property as Uses Transaction.
For simplicity, we will consider the StoreAdd method of the clsStore class and the ProductAdd method of the ClsProduct class.
Let's look at the code for the clsStore class.
Implements IobjectConstruct
The IObjectConstruct interface is implemented to control the Object Construction process of the COM+ object and pass parameters administratively from the Component Services Manager. When you enable Object Construction for your component, (In the Component Services Manager, right click the component and go to the Activation tab and check the Enable Object Construction checkbox) it allows you to specify the Constructor String.
Once this is done, when the object is constructed, COM+ calls the Construct method of IObjectConstruct and passes in the IObjectConstructString object. This has a property called ConstructString that gives the constructor string specified in the Component Services Manager. The ConnectionString for the database is a good example that can be stored in component services manager.
Private Sub IObjectConstruct_Construct(ByVal pCtorObj As
Object)
'Assign the Connection String to the module level
variable
mstrConnectionString = pCtorObj.ConstructString
End
Sub
The StoreAdd method takes several parameters related to a store.
Here we call the utility function RunSPReturnRS that has the standard code to execute a stored procedure based on the parameters supplied to it. RunSPReturnRS takes a ConnectionString as an argument.
Set rstStore = RunSPReturnRS(mstrConnectionString,
_
"StoreAdd", _
Array("@sStoreName", adVarChar, 128,
strStoreName), _
Array("@sAddress", adVarChar, 128, strStoreAddress),
_
Array("@sCity", adVarChar, 64, strCity), _
Array("@sState",
adVarChar, 10, strState), _
Array("@sZip", adVarChar, 10, strZip),
_
Array("@sPhone", adVarChar, 128, strPhone),
_
Array("@sHomePageURL", adVarChar, 128, strHomePageURL))
This line returns the newly added StoreId to the caller.
StoreAdd = rstStore("StoreID").Value
Before exiting the function, SetComplete of ObjectContext is called to set the Done and the Consistency bit to true that indicates that the object can be deactivated and all the data manipulation operations can be committed.
GetObjectContext.SetComplete
In the Error Handler, the SetAbort method of the ObjectContext interface is called. This call negates the execution of all the database manipulation statements by setting the Consistency bit to false and the Done bit to True.
GetObjectContext.SetAbort
And the error is raised back to the client using the Err.Raise statement
Err.Raise Err.Number,Err.Source & ".StoreAdd",Err.Description
The code for the clsProduct class's ProductAdd method is similar to the above method except that it executes a different stored procedure and it is available for download. Now the COM object (Unmanaged code) we created should be made accessible to the .NET Managed code environment.
Difference between Managed code and Unmanaged Code
When you compile your Visual Studio.NET source code, the compiler gives out MSIL (Microsoft Intermediate Language). MSIL code converts into native code at runtime by a Just In Time (JIT) Compiler. MSIL code should supply all the metadata necessary for the Common Language runtime (CLR) to provide services such as Garbage Collection, Memory Management, Cross-Language Integration, Code Access Security etc. All code that is based on MSIL executes as Managed Code.
Unmanaged code is the one that was created without taking the requirements of the CLR into account. This code executes in the runtime environment with minimal services from runtime. For example, the above COM object that we created using VB 6 executes as unmanaged code.
Invoking Unmanaged Code from Managed Code
Since web services are written using managed code, invoking unmanaged Code (COM object) from managed code (.NET Application) requires a Runtime Callable Wrapper (RCW) that wraps around the COM objects and exposes them to the clients as if it is a managed code.
Whenever any request for a COM object is made, the request is received by the RCW that marshals the requests to the underlying COM object. Once the COM object is finished with its execution, it returns the results back to the RCW that in turn forwards the results to the caller.
You can create a RCW for a COM object using any of the following methods:
When you add a reference to the COM object (by navigating to the
COM tab of the Add Reference Dialog box) to a Visual Studio.NET application, it
automatically creates a RCW for you.
You can use the tool
tlbimp(TypeLibrary Importer) that is part of the Microsoft.NET Framework that
converts the type library of a COM object to a .NET assembly Meta data. For
example, Tlbimp C:\Projects\ASPToday\Store_DB\Store_DB.dll
/out:Store_DB_Meta.dll creates a metadata Assembly named Store_DB_Meta.dll for
the COM component Store_DB.dll. The switch out: indicates the name of the output
file to be created.
You can also use the TypeLibConverter class that is
a member of the System.Runtime.InteropServices to generate the assembly
metadata.
Implementation of the Web Service
Let us create the Web Callable method called AddStoreAndProducts that takes the information about a Store and the various products available in that Store. This web service internally calls two different COM objects to carry out the execution.
It creates an instance of the clsStore class and calls the StoreAdd method to add Store information into the Store Table and the method returns the StoreId of the newly inserted Store to the WebService. This returned StoreID is used when inserting all the products in that store.
Since both the VB objects are marked with MTSTransactionMode property as Uses Transaction, they will take part in the same transaction as that of the caller, if the caller is running in a Transaction. Otherwise they will not run in any Transaction. In this case, since web services are marked to run in a transaction, the transaction context of the web service will be inherited by the COM objects and they will also take part in the same transaction as that of the web service which will ensure that either both Store and Product Information are written to database in its entirety or nothing is written.
Due to the Stateless nature of the web, Web services can only participate as the root object of a transaction. For example, if a web service method with the TransactionMode property of Required or RequiresNew calls another web method with a TransactionMode property of Required, each web service method will have their own separate Transaction.
Web services support the following values for the TransactionMode enum property.
NotSupported
Required
RequiresNew
Supported
Let's dissect the code of the AddStoreAndProducts
method.
The TransactionMode property for the Web Method is specified along with the WebMethod Attribute.
[WebMethod(TransactionMode = TransactionMode.Required)]
public bool AddStoreAndProducts(String StoreName,String
StoreAddress,
String StoreCity,String StoreState,String
StoreZip,
String StorePhone,String StoreHomePageURL,String
ProductList,String ProductDescList,String ProductPrice)
{
clsStore objStore = new clsStore();
clsProduct
objProduct = new clsProduct();
int StoreID =
objStore.StoreAdd(StoreName,
StoreAddress,StoreCity,StoreState,StoreZip,StorePhone,StoreHomePageURL);
String[] arrProductName;
String[]
arrProductDescription;
String[] arrProductPrice;
The Split method that is popular among ASP Programmers now has become part of the class Regex that is contained in the namespace System.Text.RegularExpressions. The comma-delimited lists of the products are assigned to an array by using the Split function.
arrProductName =
Regex.Split(ProductList,",");
arrProductDescription=
Regex.Split(ProductDescList,",");
arrProductPrice =
Regex.Split(ProductPrice,",");
int len =
arrProductName.Length;
We will loop through the array and for every element in the array; we will call the ProductAdd method and insert all the products into the Product table.
for(int
i=0;i<len;i++)
{
objProduct.ProductAdd(arrProductName,arrProductDescription,StoreID,arrProductPrice);
}
return true;
}
To see some of the real time, working web services in action, please take a look at Microsoft.com Demo Web Services.
Prerequisites
To execute the code that is available for download, you need to
have the following software installed on your machine:
Windows 2000
Professional/Server
SQL Server 2000 Beta2 or SQL Server 7.0
Visual Studio.NET Beta1
Visual Studio 6.0
What does the future hold for us?
In the future, Web services will play an important role in allowing disparate applications written using different tools in different platforms to interact with each other using Internet as the glue. Web services will also introduce new business opportunities by exposing Software As Services. For Example, a credit card company can expose Credit card Validation as a Service for other businesses to take advantage of it. Since Web services are based on Internet standards, they do not need to be written using Visual Studio.NET and they do not have any dependency towards the Windows Operating System. But the great advantage to using Microsoft Technologies is that it is very easy, simple and automatic to create and assemble rich Next Generation Web Services.
I hope you find this article useful and Thanks for reading.