Consuming Web Service using ASP.NET AJAX
Introduction
ASP.NET AJAX is all set to provide the power of
Asynchronous JavaScript and XML to your web sites. AJAX makes the web pages more
responsive and interactive by reducing the page refreshes or post backs. AJAX
harnesses the power of client side JavaScript and XML HTTP object to provide
these features. Though AJAX is essentially a client side technique most of the
real world cases call for server side processing. Most commonly the data
manipulated by your web site will be residing in some RDBMS on the server. To
make AJAX really useful there has to be a easy and robust way to deal with this
server side data. Fortunately ASP.NET AJAX provides a sound infrastructure to do
just that. AJAX communication happens between your browser and the server over
internet. Naturally, web services can play a significant role in data transport
and overall communication between client and the server. In this article we
are going to see how ASP.NET AJAX can consume ASP.NET Web Services.
Example Scenario
As an example we will develop a web form that acts as a
data entry page for Employees table of Northwind database. We will a web
service that will allow us to SELECT, INSERT, UPDATE and DELETE employees. This
web service will be consumed our data entry page using ASP.NET AJAX
features.
Creating web service
To begin with, create a new web site using Visual Studio
2005. Notice that installing ASP.NET AJAX will add certain
project templates to the New Web Site dialog. The Figure 1 shows the New
Web Site dialog with the templates I just mentioned.

The web site created using the "ASP.NET AJAX Enabled Web
Site" template is different than normal Web Site in the following ways:
Of course, you can modify a normal web site to make it
AJAX enabled but this template simplifies your job.
Now that you have created a new web site, add a new web
service to it and name it as EmployeeService.asmx. The EmployeeService will
contain five web methods. These web methods are mentioned in the following
table.
Method Name |
Description |
GetEmployees() |
This method returns a list of employees from
Employees table. The list is returned in the form of an array of Employee
objects |
GetEmployee() |
This method accepts an EmployeeID and returns its
details as an Employee object |
Insert() |
This method adds a new employee to the Employees
table |
Update() |
This method updates an existing employee from the
Employees table |
Delete() |
This method deletes and existing employee from the
Employees table |
p>
The GetEmployees() and GetEmployee() methods return data
in the form of Employee object(s). Hence, first you need to create a class
called Employee. To do so, right click on the App_Code folder of your web site
and choose "Add New Item...". Using the "Add New Item" dialog add a new class
called Employee. The complete code that makes the Employee class is shown below:
public class Employee
{
private int intEmployeeID;
private string strFirstName;
private string strLastName;
public int EmployeeID
{
get
{
return intEmployeeID;
}
set
{
intEmployeeID = value;
}
}
public string FirstName
{
get
{
return strFirstName;
}
set
{
strFirstName = value;
}
}
public string LastName
{
get
{
return strLastName;
}
set
{
strLastName = value;
}
}
}
The Employee class declares three provate variables for
storing employee id, first name and last name respectively. The three variables
are wrapped inside three public properties namely EmployeeID, FirstName and LastName.
Open web.config file and add a <connectionStrings>
section as shown below:
<connectionStrings>
<add name="connstr" connectionString=
"data source=.\sqlexpress;
initial catalog=northwind;
integrated security=true"/>
</connectionStrings>
Here, we store a database connection string that points
to the Northwind database. Make sure to change SQL Server name/IP and authentication details
to match your environment.
Now open EmployeeService.cs and
add the following code:
private string strConn =
"";
public EmployeeService()
{
strConn = ConfigurationManager.ConnectionStrings
["connstr"].ConnectionString;
}
The code reads the connection string from the config file
using ConfigurationManager class and stores it in a class level variable called strConn. This variable is used further by all
the other web wethods.
Now add GetEmployees web
method as shown below:
[WebMethod]
public Employee[] GetEmployees()
{
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "select employeeid,firstname,
lastname from employees";
SqlDataReader reader = cmd.ExecuteReader();
List<Employee> list = new List<Employee>();
while (reader.Read())
{
Employee emp = new Employee();
emp.EmployeeID = reader.GetInt32(0);
emp.FirstName = reader.GetString(1);
emp.LastName = reader.GetString(2);
list.Add(emp);
}
reader.Close();
cnn.Close();
return list.ToArray();
}
The code creates a new SqlConnection and SqlCommand
objects. It then executes a SELECT query and fetches EmployeeID, FirstName and
LastName columns from Employees table. The results are retrieved as
SqlDataReader instance. Then a generic based List of Employee objects is
created. A while loop iterates through the SqlDataReader. With each iteration a
new Employee object is created and its EmployeeID, FirstName and LastName
properties are set. The Employee object is then added to the List. After the
while loop is over, SqlDataReader and SqlConnection are closed. The return type
of GetEmployees() web method is an array of Employee objects. Hence, the generic
List is converted into Employee array using ToArray()
method of List class.
Next, add a web method called
GetEmployee() as shown below:
[WebMethod]
public Employee GetEmployee(int pEmployeeId)
{
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "select employeeid,firstname,
lastname from employees where employeeid=@id";
SqlParameter id = new SqlParameter("@id", pEmployeeId);
cmd.Parameters.Add(id);
SqlDataReader reader = cmd.ExecuteReader();
Employee emp = new Employee();
while (reader.Read())
{
emp.EmployeeID = reader.GetInt32(0);
emp.FirstName = reader.GetString(1);
emp.LastName = reader.GetString(2);
}
reader.Close();
cnn.Close();
return emp;
}
The GetEmployee() web method accepts an employee id that
is to be returned. The code is very similar to what we discussed earlier but
this time it fetches only one employee. Note that we have used SqlParameter to
represent the passed EmployeeID.
Now you can add Insert(), Update() and Delete() web
methods as shown below:
[WebMethod]
public int Insert(string pFirstName, string pLastName)
{
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "insert into employees(firstname,lastname)
values (@fname,@lname)";
SqlParameter fname = new SqlParameter("@fname", pFirstName);
SqlParameter lname = new SqlParameter("@lname", pLastName);
cmd.Parameters.Add(fname);
cmd.Parameters.Add(lname);
int i = cmd.ExecuteNonQuery();
cnn.Close();
return i;
}
[WebMethod]
public int Update(int pEmployeeId,string pFirstName,
string pLastName)
{
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "update employees set firstname=@fname,
lastname=@lname where employeeid=@id";
SqlParameter fname = new SqlParameter("@fname", pFirstName);
SqlParameter lname = new SqlParameter("@lname", pLastName);
SqlParameter id = new SqlParameter("@id", pEmployeeId);
cmd.Parameters.Add(fname);
cmd.Parameters.Add(lname);
cmd.Parameters.Add(id);
int i = cmd.ExecuteNonQuery();
cnn.Close();
return i;
}
[WebMethod]
public int Delete(int pEmployeeId)
{
SqlConnection cnn = new SqlConnection(strConn);
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "delete from employees where employeeid=@id";
SqlParameter id = new SqlParameter("@id", pEmployeeId);
cmd.Parameters.Add(id);
int i = cmd.ExecuteNonQuery();
cnn.Close();
return i;
}
The Insert() web method accepts first name and last name
of the employee to be added. It then creates an instance of SqlConnection and
SqlCommand each and execute the INSERT statement using ExecuteNonQuery()
method of SqlCommand object. Similarly, the Update() web method accepts employee
id to be updated along with new values for first name and last name and fires an
UPDATE statement. Finally, the Delete() web method accepts an employee id that
is to be deleted and fires a DELETE statement.
This completes your web service. Uptill this point we
have not done anything specific to AJAX functionality. Now it's time to
just that. Modify your web service class defination as shown below:
using System.Web.Script.Services;
...
...
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class EmployeeService : System.Web.Services.WebService
{
...
...
Notice the lines marked in bold letters. We have
imported a namespace System.Web.Script.Services. This namespace comes from
System.Web.Extensions assembly. The [ScriptService] attribute used later is
supplied by this namespace. The [ScriptService] attribute enables the web
service to be called from the client side JavaScript i.e. ASP.NET AJAX.
That's it! We are now ready to consume our web service
from ASP.NET AJAX.
How to consume web service?
In this section we will create a web form that acts as a
data entry page for the Employees table by consuming the web service we just
created. To being, add a new web form called EmployeeServiceClient.aspx to the
web site. Open the toolbox by selecting View > Toolbox menu option. On the
toolbox locate a node titled AJAX Extensions. The Figure 2 shows this node.
The AJAX Extensions node displays all the ASP.NET
AJAX components that you can use on a web form. Drag and drop a ScriptManager
component on the web form. Every web form making use of ASP.NET AJAX needs one
ScriptManager component. Open the properties window for the ScriptManager.
Locate its Services property and open Service Reference collection editor as shown in Figure 3.
Click on the Add button at the bottom of the dialog and
set Path property to the virtual path of the web service i.e.
EmployeeService.asmx. Doing so will generate the following markup in the web form file.
<asp:ScriptManager ID="ScriptManager1" runat="server" >
<Services>
<asp:ServiceReference Path="EmployeeService.asmx" />
</Services>
</asp:ScriptManager>
For each web service that you want to consume there must
be one <asp:ServiceReference> element inside the <asp:ScriptManager>
section. The <asp:ServiceReference> tag registers a web service to use in the current web form.
Now design the web form as shown in Figure 4.
The web form consists of a drop down
(<SELECT>) that lists all the existing employee IDs. Once you select
a specific ID details of that employee are displayed in the two textboxes. You
can then update them. In order to add an employee you need to simply enter first
name and last name and click on Insert button. Similarly, an employee can be
deleted by selecting employee ID in the drop down and clicking on the Delete
button. A success or failure message is displayed once the INSERT, UPDATE or
DELETE operation is over. The complete markup of the web form is shown below:
<table>
<tr>
<td colspan="2">
<asp:Label ID="Label4" runat="server"
Font-Size="X-Large" Text="Employee Management">
</asp:Label></td>
</tr>
<tr>
<td style="width: 100px">
<asp:Label ID="Label1" runat="server" Text="Employee ID :">
</asp:Label></td>
<td style="width: 100px">
<select id="Select1" >
</select>
</td>
</tr>
<tr>
<td style="width: 100px">
<asp:Label ID="Label2" runat="server" Text="First Name :">
</asp:Label></td>
<td style="width: 100px">
<input id="Text1" type="text" /></td>
</tr>
<tr>
<td style="width: 100px">
<asp:Label ID="Label3" runat="server" Text="Last Name :">
</asp:Label></td>
<td style="width: 100px">
<input id="Text2" type="text" /></td>
</tr>
<tr>
<td align="center" colspan="2">
<input id="Button3" type="button" value="Insert" />
<input id="Button4" type="button" value="Update" />
<input id="Button5" type="button" value="Delete" />
</td>
</tr>
<tr>
<td align="center" colspan="2">
<span id="lblMsg" style="font-weight: bold; color: red;">
</span>
</td>
</tr>
</table>
Notice one important thing. We have not used ASP.NET
server controls such as DropDownList, TextBox and Button. Instead we have used
traditional HTML controls such as <SELECT> and <INPUT>. This is so
because we want to call our web service from client side JavaScript and not from
server side code. Also, observe that there is a <SPAN> tag at the bottom
that will be used for displaying success or failure message.
Next, add a <script> section inside the
<head> HTML element. Add a function called CallWebMethod() as shown below:
function CallWebMethod(methodType)
{
switch(methodType)
{
case "select":
EmployeeService.GetEmployees(FillEmployeeList,
ErrorHandler,TimeOutHandler);
break;
case "selectone":
var select=document.getElementById("Select1");
var empid=select.options[select.selectedIndex].value;
EmployeeService.GetEmployee(empid,DisplayEmployeeDetails,
ErrorHandler,TimeOutHandler);
break;
case "insert":
var text1=document.getElementById("Text1");
var text2=document.getElementById("Text2");
EmployeeService.Insert(text1.value,text2.value,InsertEmployee,
ErrorHandler,TimeOutHandler);
break;
case "update":
var select=document.getElementById("Select1");
var empid=select.options[select.selectedIndex].value;
var text1=document.getElementById("Text1");
var text2=document.getElementById("Text2");
var emp=new Employee();
emp.EmployeeID=empid;
emp.FirstName=text1.value;
emp.LastName=text2.value;
EmployeeService.Update(empid,text1.value,text2.value,
UpdateEmployee,ErrorHandler,TimeOutHandler);
break;
case "delete":
var select=document.getElementById("Select1");
var empid=select.options[select.selectedIndex].value;
EmployeeService.Delete(empid,DeleteEmployee,ErrorHandler,
TimeOutHandler);
break;
}
}
The CallWebMethod() function is a central function that
makes calls to the web service. The function accepts a string parameter
indicating the method to be called and then calls that method. It contains a
switch statement that checks of the method to be called. Each case block calls
one web method. Notice how the web method has been called. The ASP.NET AJAX
framework automatically creates a JavaScript proxy class for the web serive. The
proxy class has the same name as the web service. So, EmployeeService used in
the above code is not the actual web service class but a JavaScript proxy class.
The proxy contains all the web methods of the original web service. In addition
to the original web method parameters, each method can have three extra parameters.
The first extra parameter is a JavaScript function
that should be called when the web method call is successfully completed.
Remember that any AJAX communication between client and server is anynchronous
and hence this function is necessary to capture the return value of web method.
The Second parameter is a JavaScript function that is to be called in the
event of error. Finally, the thrid extra parameter is a JavaScript function that
is to be called if timeout occurs during the web method call.
In the first case ("select") we simply call the
GetEmployees() method. In the second case "selectone") we call GetEmployee()
method. The employee ID is picked up from the drop down using traditional
JavaScript code. Similarly, third, fourth and fifth case blocks call Insert(),
Update() and Delete() methods respectively.
In the code shown above we have used five JavaScript
functions that are called when the respective web method calls are successful.
They are - FillEmployeeList(), DisplayEmployeeDetails(), InsertEmployee(),
UpdateEmployee() and DeleteEmployee(). Each of these functions accept a single
parameter that represents the return value of the corresponding web method.
These functions are discussed next.
function FillEmployeeList(result)
{
var select=document.getElementById("Select1");
for(var i=0;i<result.length;i++)
{
var option=new Option(result[i].EmployeeID,result[i].
EmployeeID);
select.options.add(option);
}
}
function DisplayEmployeeDetails(result)
{
var text1=document.getElementById("Text1");
var text2=document.getElementById("Text2");
text1.innerText=result.FirstName;
text2.innerText=result.LastName;
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="";
}
function InsertEmployee(result)
{
if(result>0)
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Employee added successfully!";
}
else
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Error occured while adding new employee!";
}
}
function UpdateEmployee(result)
{
if(result>0)
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Employee updated successfully!";
}
else
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Error occured while updating
the employee!";
}
}
function DeleteEmployee(result)
{
if(result>0)
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Employee deleted successfully!";
}
else
{
var lblMsg=document.getElementById("lblMsg");
lblMsg.innerText="Error occured while deleting employee!";
}
}
The FillEmployeeList() function receives an array of
Employee objects as a parameter. Recollect that our GetEmployees() web method
returns an array of Employee objects. It then iterates through the array. With
each iteration a new OPTION element is created and added to the drop
down (i.e. <SELECT>). The DisplayEmployeeDetails() function receives an Employee object containing
details about an employee. It simply displays those details in the two textboxes.
The InsertEmployee(), UpdateEmployee() and DeleteEmployee() functions receive an integer indicating the number of records affected by
the INSERT, UPDATE and DELETE operation respectively.
If the result is greater than zero that indicates success and thy
display a success message in the <SPAN>
tag. Otherwise they display an error message. When
your page is displayed for the first time you need to populate
the drop down list with the list of existing employee IDs. This is
done by calling CallWebMethod() method in a special function named pageLoad()
function pageLoad()
{
CallWebMethod("select");
}
The pageLoad() function gets called
automatically when the web page is loaded in the client browser. Finally, the
error handler function and timeout handler function look as shown below:
function TimeOutHandler(result)
{
alert("Timeout :" + result);
}
function ErrorHandler(result)
{
var msg=result.get_exceptionType() + "\r\n";
msg += result.get_message() + "\r\n";
msg += result.get_stackTrace();
alert(msg);
}
The TimeOutHandler() function is called
whenever a web method times out. It simply shows an alert to the
user. The ErrorHandler() function is called whenever
there is any error. The result parameter received
by this method provides three methods viz. get_exceptionType(), get_message() and get_stackTrace(). These
three methods return the type of exception, detailed error message and stack trace
respectively. The ErrorHandler() function simply displays an alert to the end user.
Testing the web page
Now that we have developed the web service and the
client application, it's time to test them. Run the web form and try inserting,
updating or deleting employees. The Figure 5 shows the web form after updating
an employee.
To test the error handler function, deliberately
initialize the connection string to empty string and run the web page again.
This time you should see an alert as shown below:
Calling External Web Services
In our example the EmployeeService was part of our web
site. At times you may need to call web services that are not hosted by your
domain at all. ASP.NET AJAX internally relies on XML HTTP object. For security
reasons XML HTTP object can not communicate outside the originating web
site. That means above technique can not be used for external web services.
Unfortunately, ASP.NET AJAX do not have direct solution to this issue (at least
in RC release). Microsoft, however, has released a "bridge" technology that
is still in CTP stages. Using the bridge you call a wrapper class
residing in your web site which in turn calls the actual web service. In current
RC release you can create a wrapper web service in your web site which in turn
calls the original web service. From the client application you can then call
your wrapper web service and achieve the communication. The overall outline of
steps is as follows:
-
Add a web reference to the external web service in
your web site
-
Create a new web service in your web site
-
Provide wrapper web methods in the newly created web
service that call the external web methods via web reference
-
Call this newly added web service from the client
application as illustrated in this article
Summary
ASP.NET AJAX provides complete infrastructure to call
ASP.NET web services from client side JavaScript. You can easily integrate
server data with the user responsive web pages using AJAX. All you need to do is
to mark your web service with [ScriptService] attribute. The ASP.NET AJAX
framework takes care to generate a JavaScript proxy for your web service. The
proxy is then used for calling the web methods.