One of the issues developers faced when building commercial Web sites was the limitations of using a browser as the interface. For instance, there were many cases where they wanted to retrieve information from the server after the user had performed some action, like entering an employee number. To accomplish this, they would post the current page to the server, retrieve the employee information and refresh the page with the information retrieved from the server. Although this method of refreshing the whole page is very common today, it is inefficient because the Web page refreshes and re-renders an entire page of content, even if only a small percentage of the page has actually changed. You can probably notice this inefficiency when searching a catalog or a search engine. The delays and waste of resources can be significant. However, if the same functionality can be accomplished without refreshing the browser page, the user experience can be greatly enhanced. To accomplish this, there needs to be a way to execute a piece of code on the server without leaving the current page, which is what exactly the Web service behavior is used for. In this case, the piece of code that is executed on the server is a Web service method code, and the browser is the one that invokes that piece of Web service code from the browser without leaving or even refreshing the current page.
When using Web service behavior, you just send out a request to execute a specific Web service method from within a Web page from the client browser. On the server side, the request is received by the ASP.NET runtime, which invokes the Web service method passing in the related parameters. After the Web service has finished its execution, it communicates the results back to the caller, which is then displayed or processed by the browser. As a result, you can build typical client/server communications despite the stateless nature of the underlying HTTP protocol. Another advantage of the Web service behavior is that it requires only one file (webservice.htc) on the client side to accomplish this. Using the Web service behavior, you can also asynchronously invoke a Web service method. This capability is very powerful in that it can be used to create rich user experiences on the client side. For example, you could use the Web service behavior to ask the server to validate data while the users continue working with the same page. Once the function call returns, you can then get the result of the execution and then communicate that to the user.
Web Service Behavior
The Web service behavior is implemented with an HTML Component (HTC) file as an attached behavior, and it can be used in Internet Explorer 5 and later versions. As mentioned before, the Web service behavior provides a cross-platform way of invoking a remote Web service by leveraging industry standard protocols such as HTTP, SOAP, and XML. One of the important features of the Web service behavior is that it allows you to use this functionality without having expert knowledge of SOAP. Basically the Web service behavior simplifies the remote invocation of Web services by handling the communication of the SOAP data packets between the browser and the Web services. You don't have to worry about assembling and disassembling SOAP messages. All the SOAP-specific handling code is encapsulated inside the behavior, simplifying the client-side script in the main Web page.
The Web service behavior is a JavaScript file embedded in a Web page using specific IE behavior syntax. By exposing properties and methods to client-side scripts, the Web service behavior assembles messages as well as disassembles responses that are sent back by the Web services. The objects that are exposed by the behavior not only enable a cleaner error handling approach but also provide easy access to the returned data. The Web service behavior receives method calls from the client-side script and sends the requests to the Web service using SOAP messages. The results are returned to the client script and processing continues. The Web page can then use the information in whatever context is required, such as updating some portion of the page, sending error messages, and so on.
A key feature of the Web service behavior is that it enables client-side script to access a Web service without requiring navigation to another URL. The following listing details the important methods supported by the Web service behavior:
createUseOptions - Allows us to preserve authentication information across remote method invocations. Can be very useful when using SSL to communicate with the remote web service
callService - Allows us to invoke the remote web service method asynchronously
useService - Allows us to establish a friendly name for the web service that can be used while invoking the web service
To use the behavior in a Web page in IE 5.0 and above, you will need to download the webservice.htc behavior file and save it in the same folder as your Web page. The file can be downloaded from the following location: http://msdn.microsoft.com/downloads/samples/internet/behaviors/library/webservice/webservice.htc
Implementation
Now that you understand the basics of the Web service behavior, take a look at this example application that demonstrates how to leverage the Web service behavior in ASP.NET applications. In this example, you will create a simple application which allows you to retrieve employee information from the Northwind database. This example application will allow you to search for employees based on the employee id
Creation of Employee Web Service
In this section, you will create a new Visual C# Web Service project named EmployeeWebService. Once the project is created, you will rename the default Web service class from Service1 to EmployeeService. Then you will import the following namespaces for performing data accessing and handling XML data.
using System.Data.SqlClient;
using System.Xml;
[WebMethod]
public XmlDocument GetEmpDetailsByEmpID (int employeeID)
{
string connString =
System.Configuration.ConfigurationSettings.AppSettings["connectionString"];
SqlConnection sqlConnection = new SqlConnection(connString);
try
{
DataSet employeeDataset = new DataSet("EmployeesRoot");
//Pass in the name of the stored procedure to be executed and the SqlConnection
object as the argument
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommand command = new SqlCommand("Select * from Employees Where EmployeeID ="
+ employeeID.ToString(),sqlConnection);
//Set the SqlCommand object properties
command.CommandType = CommandType.Text;
adapter.SelectCommand = command;
//Fill the Dataset with the return value from the stored procedure
adapter.Fill(employeeDataset,"Employees" );
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(employeeDataset.GetXml());
return xmlDoc;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (sqlConnection.State == ConnectionState.Open)
{
sqlConnection.Close();
}
}
}
The attribute WebMethod indicates that the method is to be exposed as a Web-callable method. When it is deployed, ASP.NET runtime provides all the plumbing required to make this method callable across the Internet using protocols like XML, HTTP, and SOAP.
[WebMethod]
As the name of the method suggests, the GetEmpDetailsByEmpID takes the employeeID as an argument and returns the details of that employee in the form of an XmlDocument.
public XmlDocument GetEmpDetailsByEmpID(int employeeID)
{
string connString = System.Configuration.ConfigurationSettings.AppSettings
["connectionString"];
The above lines retrieve the connection string from the <appsettings> section of the web.config file using the AppSettings property of the ConfigurationSettings class. The connection string is defined as follows in the web.config file:
In this line, you create an instance of the SqlConnection object, passing to it the connection string that is required for establishing connection to the database.
SqlConnection sqlConnection = new SqlConnection(connString);
Then you enclose all the executable statements in a try...catch block to handle any errors that may occur during the execution of the following statements.
try
{
DataSet employeeDataset = new DataSet("EmployeesRoot");
SqlDataAdapter adapter = new SqlDataAdapter();
Next, you create an instance of SqlCommand object, passing to its constructor the SQL statement that you want to execute and the SqlConnection object that you created in the previous step.
SqlCommand command = new SqlCommand("Select * from Employees Where EmployeeID ="
+ employeeID.ToString(),sqlConnection);
You then set the SelectCommand property to appropriate value to indicate that you want to execute a SQL statement.
//Set the SqlCommand object properties
command.CommandType = CommandType.Text;
Here you set the SelectCommand property of the SqlDataAdapter object to the previously created SqlCommand object.
adapter.SelectCommand = command;
Now use the Fill method to retrieve the data from the data source by executing the previously assigned SQL statement against the data source.
//Fill the Dataset with the return value from the stored procedure
adapter.Fill(employeeDataset,"Employees" );
Once you have the employee information in the form of a dataset, you then retrieve its contents and pass it as an argument to the LoadXml method of the XmlDocument object. Finally, you return the XmlDocument object to the caller of the Web service.
In the Finally block, you inspect the State property to verify whether the Connection is still open. If the connection is still open, you close it by calling the Close method of the connection object.
Now that you have created the Web service, test its functionality by right clicking on the EmployeeService.asmx file and selecting Build and Browse. You should get a screen that is similar to the following.
The above code relies on the behavior functionality built into IE 5 (and higher) to identify the location of the JavaScript file that will be used to call Web services. As discussed previously, the webservice.htc file should be available on the same folder as that of the Web page. It is important to note that the loading of the behavior file occurs on the client rather than on the server.
Once embedded, the behavior can be invoked and linked to a WSDL 1.1-compliant Web service using JavaScript code. This is accomplished by referencing the embedded behavior id (service in the previous code) and calling its useService method:
You invoke the useService method in the onload event handler for the page to ensure that the Web service is mapped before any methods are invoked on the Web service.
The useService method accepts the following arguments.
Path to the WSDL file for the Web service
A friendly name that can be later used to reference the Web service. This name will be used each time the web methods in the EmployeeService.asmx file are called using the behavior.
Now that the Web service is setup for access, you invoke the Web service methods asynchronously in two steps. The advantage of asynchronous invocation is that the Web page does not have to keep waiting for the Web service to return. In the first step, you invoke the Web method and supply a callback function as an argument. As a second step, the call back function gets fired when the Web service returns after executing the method.
Here is the complete listing of the source code.
<%@ Page language="c#" Codebehind="EmployeeServiceClient.aspx.cs"
AutoEventWireup="false" Inherits="
EmployeeWebServiceClient.EmployeeServiceClient" %>
<HTML>
<HEAD>
<title>Employee Details</title>
<SCRIPT LANGUAGE="JScript">
//Declare a module level variable to capture the event id
var iCallID ;
function GetEmployeeDetails()
{
// Call the GetEmployeeDetails method on the svcEmployee web service
iCallID =
service.svcEmployee.callService(DisplayResults,"GetEmpDetailsByEmpID",txtEmploye
eID.value);
}
function DisplayResults(result)
{
var strXML,objXMLNode,objXMLDoc,objEmployee,strHTML;
//Check if the event id is the same
if (iCallID != result.id)
return;
if(result.error)
{
// Pull the error information
var faultCode = result.errorDetail.code;
var faultString = result.errorDetail.string;
alert("ERROR: Code = " + faultCode + ", Fault String=" + faultString);
}
else
{
//Get the resultant value into a local variable
objXMLNode = result.value;
objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
//Load the returned XML string into XMLDOM Object
objXMLDoc.loadXML(objXMLNode.xml);
//Get reference to the Employees Node
objEmployee =
objXMLDoc.selectSingleNode("GetEmpDetailsByEmpIDResult").selectSingleNode("Emplo
yeesRoot").selectSingleNode("Employees");
//Check if a valid employee reference is returned from the server or not
strHTML = "<font color='#0000FF'>";
if (objEmployee != null)
{
//Dynamically generate HTML and append the contents to a string variable
strHTML += "<br><br>Employee ID :<b>" +
objEmployee.selectSingleNode("EmployeeID").text + "</b><br><br>";
strHTML += "Employee First Name :<b>" +
objEmployee.selectSingleNode("FirstName").text +
"</b><br><br>";
strHTML += "Employee Last Name :<b>" +
objEmployee.selectSingleNode("LastName").text + "</b><br><br>";
strHTML += "Employee Title :<b>" +
objEmployee.selectSingleNode("Title").text + "</b><br><br>";
strHTML += "Employee Title :<b>" +
objEmployee.selectSingleNode("Title").text + "</b><br><br>";
strHTML += "Title Of Courtesy:<b>" +
objEmployee.selectSingleNode("TitleOfCourtesy").text + "</b><br><br>";
strHTML += "Postal Code:<b>" +
objEmployee.selectSingleNode("PostalCode").text + "</b><br><br>";
}
else
{
strHTML += "<br><br><b>Employee
not found</b>";
}
strHTML += "</font>"
//Assign the dynamically generated HTML into the div tag
divContents.innerHTML = strHTML;
}
}
function init()
{
// Create an instance of the web service and call it svcEmployee
service.useService("http://localhost/MyProjects/15Seconds/WebServiceBehavior/Emp
loyeeWebService/EmployeeService.asmx?WSDL","svcEmployee");
}
</SCRIPT>
</HEAD>
<body onload="init()">
<div id="service" style="BEHAVIOR: url(webservice.htc)"></div>
<H1 align="center">
<font color="#800080">Employee Details</H1>
</FONT>
<br><br>
<P align="left"><font color="#800080"><b>Enter the
Employee ID:</b></font> <INPUT
id="txtEmployeeID" name="txtEmployeeID" style="LEFT: 149px; TOP:
72px"><INPUT id="btnAdd" type="button" value="Get Employee Details"
name="btnGetEmployee" onclick="return GetEmployeeDetails()"></P><P></P>
<div id="divContents">
</div>
<P></P>
</body>
</HTML>
In the GetEmployeeDetails method, you invoke the Web service web method passing in the callback method name and the input parameters to the Web service as arguments. This is accomplished by invoking the callService method of the Web service behavior.
function GetEmployeeDetails()
{
// Call the GetEmployeeDetails method on the svcEmployee web service
iCallID =
service.svcEmployee.callService(DisplayResults,"GetEmpDetailsByEmpID",txtEmploye
eID.value);
}
The callService method returns a unique identifier that can later be used to identify the Web service call. This is required if you are making multiple Web service calls asynchronously and then assemble their results together in the client browser itself. In this case, you match this ID with the ID that is returned as a property of the result object. This matching is done in the callback function, which you will look at it in a moment.
Since you specified the DisplayResults function as the callback function, the DisplayResults method will automatically get invoked, after the Web service has finished its execution.
function DisplayResults(result)
{
var strXML,objXMLNode,objXMLDoc,objEmployee,strHTML;
In the following lines of code, you match the ID of the result object with the ID that was already returned by the callService method.
//Check if the event id is the same
if (iCallID != result.id)
return;
Here you check the error property to check if there was any error during the execution of the Web service. If an error occurred, you then display the error information in a message box. If there is no error, you then process the returned results and display them in a HTML DIV tag.
if(result.error)
{
// Pull the error information
var faultCode = result.errorDetail.code;
var faultString = result.errorDetail.string;
alert("ERROR: Code = " + faultCode + ", Fault String=" + faultString);
}
else
{
//Get the resultant value into a local variable
objXMLNode = result.value;
objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
//Load the returned XML string into XMLDOM Object
objXMLDoc.loadXML(objXMLNode.xml);
//Get reference to the Employees Node
objEmployee =
objXMLDoc.selectSingleNode("GetEmpDetailsByEmpIDResult").selectSingleNode("Emplo
yeesRoot").selectSingleNode("Employees");
//Check if a valid employee reference is returned from the server or not
strHTML = "<font color='#0000FF'>";
if (objEmployee != null)
{
//Dynamically generate HTML and append the contents to a string variable
strHTML += "<br><br>Employee ID :<b>" +
objEmployee.selectSingleNode("EmployeeID").text +
"</b><br><br>";
strHTML += "Employee First Name :<b>" +
objEmployee.selectSingleNode("FirstName").text +
"</b><br><br>";
strHTML += "Employee Last Name :<b>" +
objEmployee.selectSingleNode("LastName").text +
"</b><br><br>";
strHTML += "Employee Title :<b>" +
objEmployee.selectSingleNode("Title").text +
"</b><br><br>";
strHTML += "Employee Title :<b>" +
objEmployee.selectSingleNode("Title").text +
"</b><br><br>";
strHTML += "Title Of Courtesy:<b>" +
objEmployee.selectSingleNode("TitleOfCourtesy").text +
"</b><br><br>";
strHTML += "Postal Code:<b>" +
objEmployee.selectSingleNode("PostalCode").text +
"</b><br><br>";
}
else
{
strHTML += "<br><br><b>Employee
not found</b>";
}
strHTML += "</font>"
//Assign the dynamically generated HTML into the div tag
divContents.innerHTML = strHTML;
}
}
In the above example, you used a callback function that was specified at the time of invoking the Web service to process the results returned by the Web service. As an alternative approach, you can also specify a callback function while declaring the DIV tag (that is used to include the Web service behavior in the page). For example, in the following line, you specify the callback function by using the onresult event handler supported by the web service behavior.
Once you have the above declaration in place, you can then process the results returned by the Web service in the DisplayResults function. The following lines of code illustrate an example implementation of the DisplayResults function.
function DisplayResults()
{
//Check if the event id is the same
if (iCallID != event.result.id)
return;
if(event.result.error)
{
var faultCode = event.result.errorDetail.code;
var faultString = event.result.errorDetail.string;
alert("ERROR: Code = " + faultCode + ", Fault String=" +
faultString);
}
else
{
//Display the resultant value
alert(event.result.value);
}
}
As you can see from the code, the event object is used to get reference to the result object that contains the results of the Web service call.
Put It All Together
If you navigate to the above ASP.NET page using a browser, you will see an output that is somewhat similar to the following. In the employee textbox, enter a valid Employee ID and click on Get Employee Details to invoke the remote Web service. This will result in an asynchronous invocation of the Web service and the result from the Web service will be displayed in the DIV element of the Web page.
It's important to re-emphasize that for the IE Web service behaviors, you need IE 5 or higher, so this technique could be a strong candidate for use in intranet applications where you are certain about the type of browsers used by your users.
Conclusion
In this article, you saw how the Web service behavior provides a streamlined approach to the problem of delivering information from the Web server to the client browser. Using the Web service behavior to invoke a remote Web method simplifies things on the client side, making the use of Web services more appealing. We also saw for ourselves how Web service behavior can help increase the user experience by providing dynamic interactive Web pages. Since the Web service behavior (webservice.htc file) encapsulates the code required for invoking a remote Web service using SOAP, you can update the behavior independently without requiring major changes to client-side script as the SOAP standard evolves.
I hope you find this article useful and thanks for reading.
About the Author
Thiru has many years of experience in architecting, designing, developing and implementing applications using Object Oriented Application development methodologies. He also possesses a thorough understanding of software life cycle (design, development and testing).
He is an expert with ASP.NET, .NET Framework, Visual C#.NET, Visual Basic.NET, ADO.NET, XML Web Services and .NET Remoting and holds MCAD for .NET, MCSD and MCP certifications.
In the second article of his series on Indigo web services, Chris Peiris explains how to host an Indigo web service and examines the IIS, self hosting, and Windows Activation Service hosting options. He then provides step-by-step instructions and sample code for an IIS-hosted and self-hosted Indigo web service. [Read This Article][Top]
In the first part of his series on Microsoft Indigo, Chris Peiris examines the basics of SOA, explains how Indigo fits into the picture and the problems it solves. He then introduces Indigo's programming model and finishes by building a sample Indigo web service using the Microsoft .Net Framework 2.0. [Read This Article][Top]
Adnan Masood concludes his discussion of Microsoft SQL Server Analysis services and Microsoft SQL Server Reporting services. In the final part, he discusses Reporting Server web services and using custom code in reports. [Read This Article][Top]
Calvin Luttrell shows how to validate e-mail addresses stored in Excel 2003 and
provides a special function for solving that pesky problem Yahoo! mail servers cause. [Read This Article][Top]
This short article describes a quick and easy way to provide some security to an ASP.NET Web service by modifying its associated documentation file. [Read This Article][Top]
Kerberos authentication is the cornerstone of Windows operating system authentication architecture. Web Services Enhancement 2.0 (WSE 2.0) extends Kerberos support to ASP.NET Web services. Chris Peiris explains the support for this new feature in WSE 2.0. [Read This Article][Top]
Chip Irek examines the architectural issues and component design issues of building a .NET application in a service-oriented architecture. [Read This Article][Top]
Thiru Thangarathinam shows how to use asynchronous Web services, Windows
Service applications, server-based timer components and .NET XML API classes to create high-performance, scalable, and flexible applications. [Read This Article][Top]
Part one showed how to transform XML data into HTML by using an XSL stylesheet from within a .NET application. This part explains how to make use of XSLT Extension objects and invoke a C# class method from an XSL stylesheet. [Read This Article][Top]
Learn how to transform XML data into HTML by using an XSL stylesheet from within a .NET application, and then implement a paging solution by declaring and supplying paging parameters to the stylesheet. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.