Creating and Consuming .NET Web Services in 5 Easy Steps Article
The
Internet has a new player on the scene. It’s been surrounded by a great
deal of hype — and even some television commercials! Apparently, this
new, "next generation technology" will change the way business is done
on the Web. It seems that soon, companies, their applications or
software, and any Internet-enabled devices will easily be able to
communicate with, and provide services to, one another regardless of
platform or language. Sounds revolutionary!
So, what exactly is it that will open these boundless lines of communication? Web Services, that’s what!
Web services give developers the ability to utilize four open Web standards:
- HTTP – Hypertext Transfer Protocol
The standard protocol used over Port 80, which traverses firewalls, and is responsible for requesting and transmitting data over the Internet.
An XML-inherent protocol that encloses a set of rules for data description and process. As a standard, this is the center-piece that complements the other three standards mentioned here.
The most common markup language in which all this information is written.
An XML-based method used to identify Web Services and their access at runtime. .NET provides a tool called WSDL.exe, which essentially makes it quite easy to generate an XML Web service as an XML file. This contains all the methods and instructions the Web Service has, and typically uses SOAP as its default.
This article will see you create and consume a data-driven .NET XML Web service in 5 quick and easy steps!
I’ll
assume that you have a decent grasp of common .NET data access, Web
server controls, such a datagrid, and some object-oriented programming
concepts. If not, don’t worry too much. If you complete the examples,
and view the results, you should have no difficulty in keeping up and
observing the causes and effects of what this tutorial entails.
Prior
to .NET, there were other alternatives that could be used to access a
Web service, such as Microsoft’s MSXML component, which enabled you to
communicate with the given Web Service over HTTP POST. However, this
process, while acceptable, is just not .NET.
Ok, let’s begin!
STEP 1 – CREATE THE WEB SERVICE
First
we’ll create the Web service function or method that’ll you’ll call (or
“expose”) over the Internet as you would any object-oriented class. The
difference is that we’ll have to incorporate and import all the
necessary Web Services namespaces, syntax and attributes, as well as our
data namespaces, in this case. As this article uses C#, any important
differences regarding VB will be shown as well.
So
go ahead and copy the code below to a file called suppliers.asmx. Save
it to your Inetpub/wwwroot folder, and then run it in your browser using
http://localhost/suppliers.asmx. What you’ll see is a list of the Web
Service Descriptions, including the Web service name and the exposed
method.
By
clicking the exposed method link, you’ll be presented with the three
main protocols that are available for your use. Just so you know, the
file with the .asmx extension is the actual Web Service file that
enables the ASP.NET runtime to return all pertinent exposed Web service
methods and information.
<%@ WebService Language="C#" Class="GetInfo" %>
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
[WebService(Description="My Suppliers List Web Service")]
public class GetInfo : WebService
{
[WebMethod(BufferResponse=true)]
public DataSet ShowSuppliers (string str)
{
SqlConnection dbConnection = new SqlConnection("server=(local);
uid=sa;pwd=;database=Northwind;");
SqlDataAdapter objCommand = new SqlDataAdapter("select
ContactName, CompanyName, City, Phone from Suppliers
where Country = '" + str + "' order by ContactName
asc", dbConnection);
DataSet DS = new DataSet();
objCommand.Fill(DS);
return DS;
dbConnection.Close();
dbConnection = null;
}
}
The
<%@ WebService Language="C#" Class="GetInfo" %>
directive sets up the file as a Web Service, gives it a name and specifies the language it uses.
Aside from your typical data namespace import, you also add the Web Services namespace:
using System.Web.Services
In VB this would be:
Imports System.Web.Services
Here I've added the[WebService(Description="My Suppliers List Web Service")]
, which gives a custom description. Next we create our class, which, in order that it be exposed, and able to inherit theWebservice
base class, must be a public class.
public class GetInfo : WebService
{
Now we mark the method that’s to be exposed via the
WebMethod
attribute. Every class that’s enclosed within a WebMethod
will be exposed as a service. Also, I boost performance by setting the BufferResponse
to true
.[WebMethod (BufferResponse=true)]
If you prefer to have the description right below the exposed
WebMethod
link, you can achieve it like this in C#:[WebMethod (Description="My Suppliers List Web
Service",BufferResponse=true)]
The VB version would incorporate the description and
BufferResponse
in one statement, namely WebMethod
,
that used angle brackets. This is placed after the class creation and
Web service inheriting line, and on the same line with, but before the
function, like so:<WebMethod(Description:="My Suppliers List Web
Service",BufferResponse:=True)>
Public Function
ShowSuppliers (ByVal str As String) As DataSet
Next,
we set up our method or VB function, which will accept a string
parameter from our drop down list and pass this to our SQL query (you’ll
see all this in detail in Step 4). If you’re really savvy you can pass
this and other parameters with the help of C# structs (scaled down
classes) to enumerate your data value types, and provide you with better
memory allocation. But for now, we’ll stick to the basics:
public DataSet ShowSuppliers (string str)
Here,
we set up our dataset method to return us exactly that. Lastly, we
perform typical data connection and access and return our results, and
we’re done! So far so good? After viewing this in your browser, the
above code should to start to make sense. Let’s go to Step 2.
STEP 2 – CONSUME THE WEB SERVICE SOURCE FILE
Next, append ?WSDL to the Web services URI (Uniform Resource Identifier) like so:
http://localhost/suppliers.asmx?WSDL
“What’s this?” you ask. This is the WSDL document that the client will use to access this service.
Even
so, you don’t have to know much about this unreadable code to produce
results, which is where the WSDL.exe command-line tool comes into play.
Due to the open protocol nature of Web services, this tool enables you
to consume non-.NET Web Services as well.
You can bypass WSDL and test the XML results instantly through HTTP GET protocol, by typing the following into your browser:
http://localhost/suppliers.asmx/ShowSuppliers?str=USA
This passes USA as a parameter to the
ShowSuppliers
class
method. Note that if you’re using .NET SDK Beta 1.1 (v.1.1.4322), it
seems to prefer HTTP POST protocol, so invoke the Web Service through
its exposed method link. The XML results? A little crazy, huh?
Nevertheless,
scroll down a little and you’ll see the query results of our class
call. Later in Step 4, all this gobbledygook will make much more
readable sense.
So, to create our proxy class sourcefile, make a batch file named makeWS.bat and type in:
[C#]
wsdl.exe /l:CS /n:WService /out:bin/GetSuppliers.cs
http://localhost/suppliers.asmx?WSDL
[VB]
wsdl.exe /l:VB /n:WService /out:bin/GetSuppliers.vb
http://localhost/suppliers.asmx?WSDL
pause
This tells our WSDL tool that
/l
is the language you’re using./n:
creates theWservice
namespace so you can reference it from your .aspx page, and finally send the C#/VB source file to the bin folder from which we'll create the dll assembly that we'll use in Step 3. Also addpause
, so after this is all said and done, you'll be prompted to press any key to continue, and more importantly, you'll know for sure that it all went according to plan.Now locate your new batch file, and double-click on it to run it. Once you've done this, you will have created, rather consumed, the proxy class or source file GetSuppliers.cs right from the .asmx file. Have a look in your bin folder.Note that you could run these commands via the command-line prompt, but for the sake of convenience, I prefer the good old' batch file. You may be wondering where the phrase "consuming" came from? In this regard, it simply refers to the presentation of the Web Service to the "consumer".
STEP 3 – BUILD OUR OBJECT
OK,
now let’s invoke the .NET C# compiler (csc.exe) or VB compiler
(vbc.exe) to convert your source file into an assembly or DLL. Create a
new .bat file named makelib.bat and type in:
[C#]
csc /t:library /out:bin\GetSuppliers.dll bin\GetSuppliers.cs
/reference:System.dll,System.Data.dll,System.Web.dll,
System.Web.Services.dll,System.XML.dll /optimize
[VB]
vbc /t:library /out:bin\GetSuppliers.dll bin\GetSuppliers.vb
/reference:System.dll,System.Data.dll,System.Web.dll,
System.Web.Services.dll,System.XML.dll /optimize
pause
All this does is compile the source file from the bin folder to a DLL of set name, to the bin folder.
The
/t:library
instructs the compiler to create a dll (dynamic link library), rather than an exe (executable) file, with /reference:
importing the necessary dll’s libraries that will be used in our Web service. Finally, we/optimize
our dll to produce smaller, faster, and more efficient output.STEP 4 – PUT IT ALL TOGETHER
Now
copy and paste the code below to an .aspx .NET page and name it. This
code is for the page that will access the assembly/DLL in your bin
folder, and with all the Web server controls, pass the appropriate
parameters to the Web Service. Go ahead and run it
(http://localhost/Websrvce.aspx).
<%@ Page Language="C#" Explicit="true" Strict="true" Buffer="true"%>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="WService" %>
<html>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e)
{
Response.Flush();
}
void SubmitBtn_Click (object src, EventArgs e)
{
int RcdCount;
string Catg = DropDown1.SelectedItem.Text;
//Instantiate DLL
GetInfo supInfo = new GetInfo();
//Pass parameter into DLL function
DataSet MyData = supInfo.ShowSuppliers(Catg);
MyDataGrid.DataSource = MyData.Tables[0].DefaultView;
MyDataGrid.DataBind();
RcdCount = MyData.Tables[0].Rows.Count;
if (RcdCount <= 0)
{
Message.InnerHtml = "<b>No results were found for <FONT
Color=Red><i>"+ Catg +"</i></font>";
MyDataGrid.Visible = false; //Hide Results until needed
}
else
{
Message.InnerHtml = "<b><FONT Color=Red><i>" + Catg +
"</i></font> has " + RcdCount + " local suppliers</b>";
MyDataGrid.Visible = true;
}
}
</script>
<body style="font: 10pt verdana">
<h4>Accessing Data with Web Services</h4>
<form runat="server">
<asp:DropDownList id=DropDown1 runat="server">
<asp:ListItem>Australia</asp:ListItem>
<asp:ListItem>Brazil</asp:ListItem>
<asp:ListItem>Canada</asp:ListItem>
<asp:ListItem>Denmark</asp:ListItem>
<asp:ListItem>Finland</asp:ListItem>
<asp:ListItem>France</asp:ListItem>
<asp:ListItem>Germany</asp:ListItem>
<asp:ListItem>Italy</asp:ListItem>
<asp:ListItem>Japan</asp:ListItem>
<asp:ListItem>Netherlands</asp:ListItem>
<asp:ListItem>Norway</asp:ListItem>
<asp:ListItem>Singapore</asp:ListItem>
<asp:ListItem>Spain</asp:ListItem>
<asp:ListItem>Sweden</asp:ListItem>
<asp:ListItem>UK</asp:ListItem>
<asp:ListItem>USA</asp:ListItem>
</asp:DropDownList>
<asp:button text="Submit" OnClick="SubmitBtn_Click" runat=server/>
<p>
<span id="Message" runat="server"/>
<p>
<ASP:DataGrid id="MyDataGrid" runat="server"
AutoGenerateColumns="True"
Width="100%"
BackColor="White"
Border="1"
BorderWidth="1"
CellPadding="1"
CellSpacing="1"
Font-Size="10pt"
HeaderStyle-BackColor="White"
HeaderStyle-ForeColor="Blue"
AlternatingItemStyle-BackColor="White"
AlternatingItemStyle-ForeColor="Black"
ShowFooter="false"
/>
</form>
</body>
</html>
How It Works
Pretty
cool, huh? Now, aside from the common data accessing and Web server
control usage, I’ll outline some specifics. By virtue of creating our
proxy class with a namespace in Step 2, we need to import it to the dll
we created in Step 3, like this:
<%@ Import Namespace="WService" %>
We then instantiate it or reference our object:
[C#]
[C#]
GetInfo supInfo = new GetInfo();
[VB]
Dim supInfo As New WService.GetInfo()
This
is our Web Service class. We then retrieve our dropdown list parameter,
and pass it to our ShowSuppliers constructor method like so:
[C#]
string Catg = DropDown1.SelectedItem.Text;
DataSet MyData = supInfo.ShowSuppliers(Catg);
[VB]
Dim Catg As String = DropDown1.SelectedItem.Text
Dim MyData As DataSet = supInfo.ShowSuppliers (Catg)
And
the rest? Just common .NET stuff, which I assume you have some grip on.
Nevertheless, it shouldn’t be too hard to follow or figure out. The
.NET documentation and Quickstart Tutorials will help with plenty more
information.
STEP 5 – SHOW IT OFF
Congratulations
are in order, for you have now built your first fully-fledged .NET XML
Data Web Service, without any IDE program such a VS .NET I might add,
and using a tool as simple as Notepad. Now can anyone really say this
was a big ordeal? I didn’t think so.
Additionally,
if you’re wondering how anyone else will "discover" your Web service,
then look no further: with the XML Web service "discovery", you can
advertise your Web service and expose its location and other
information, alongside a .discomap file providing links to your files.
XML
Web Service discovery, called DISCO for short, is Microsoft’s Web
Services Discovery tool. It "discovers" the URL for a Web Service and
saves that information in a file on your local server. Here’s how:
disco http://localhost/suppliers.asmx?DISCO
This
creates a static discovery file containing a WSDL file with all the
important information pertaining to your Web Service. Opposite this
static discovery file is a dynamic discovery .vsdisco file, which notes
all the available Web Services located within that URL.
Remember
from Step 2 the WSDL command for creating the source file with "?WSDL"?
Now, for an already "discovered" file, you can use:
wsdl.exe /l:CS /n:WService /out:bin/GetSuppliers.cs
http://localhost/suppliers.wsdl
to create your source file and, in turn, your DLL for Web Service access.
When you examine the created .disco file you’ll notice it includes the following information:
<contractRef ref="http://localhost/suppliers.asmx?wsdl"
docRef="http://localhost/suppliers.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
This
contains the link to your Web Service so others can discover it,
alongside XSD schemas, and service descriptions. Using this information
others can parse the WSDL document, build a proxy source file, and
implement this Web Service locally!
If
you do have something you’d like to share, run over to the Microsoft
UDDI (Universal Description, Discovery and Integration) Business
Registry (UBR) node at http://uddi.microsoft.com,
which is currently supported by Microsoft, IBM and Ariba. Here you can
find other available Web Services and their details, register your Web
Service, and discover more about this whole business.
A little "Asynchronousity"
Before,
I conclude this article I need to mention an additional method of
calling Web Services from your application. Web Services by default form
are called “synchronously”. As a result, both the application and its
inherent Web Service will have to finish simultaneously. As such, a
quicker method within the application will have to wait for any slower
one to finish, before the application can fully display its results,
potentially creating an unnecessary delay for the client. Likewise,
since Web Service calls make use of Port 80 for communications, they can
at times cause this type of delay. How would you be able to remedy such
a situation? With a little asynchronousity, of course!
There
are four common ways to make asynchronous method calls. Here we will
demonstrate waiting for our asynchronous call utilizing the
WaitHandle
method, which determines when the service call is complete. The other three possible methods for asynchronous calls include:- The
IsCompleted
property of theIasyncResult
returned byBeginInvoke
, within ourBeginSuppliers
Method
Given
that asynchronous calls can perform a given task without affecting
other functions around them, let’s implement our chosen technique. Going
back to Step 2, you’ll remember how we created our proxy class file for
our .asmx Web Service file.
If you took the time to inspect it, you will have noticed that, aside from our main
ShowSuppliers
Dataset function, two additional functions were listed: BeginShowSuppliers
and EndShowSuppliers
. These are the asynchronous functions that will enable us to produce asynchronous Web Service calls implementing theWaitHandle
Class’s WaitOne()
Method, and here’s how.
We do all this from the .aspx page we created in Step 4. Here’s the additional code:
//Instantiate DLL
GetInfo supInfo = new GetInfo();
// Begin the Asynchronous call to ShowSuppliers
IAsyncResult AsyncWS = supInfo.BeginShowSuppliers(Catg, null, null);
//We wait for the callback
AsyncWS.AsyncWaitHandle.WaitOne();
//We return the data bypassing the initial ShowSuppliers Method
DataSet MyData = supInfo.EndShowSuppliers(AsyncWS);
Notice
that just a few more lines of code were added. The only thing that
changed was that we added our asynchronous methods right after we
instantiated our dll. After this, we create an instance of the
IasyncResult
object that will let us know when the Web Service process has finished.IAsyncResult AsyncWS = supInfo.BeginShowSuppliers(Catg, null, null);
Here, we called the asynchronous
BeginShowSuppliers
method
that accepts our initial dropdown list parameter and two other
mandatory arguments that, in this example, were not included, and were
substituted with null
. The second argument would typically call the AsyncCallBack
object responsible for calling another method once the BeginShowSuppliers
completes, contrary to our example. The third argument would contain information about the asynchronous operation.
We then next implement the
AsyncWaitHandle
that allows us to incorporate and handle different kinds of synchronization techniques. There are a few AsyncWaitHandles
available to us. With the WaitOne
method included below, we wait for the WaitHandle
to
receive a callback signal. Further available methods include:
WaitAll(), which waits for all elements to receive a callback signal,
and WaitAny()
,
which waits for any one of the elements to receive a callback signal.
Both utilize specified arrays as one of the overloaded element
arguments, alongside an Integer for specific time interval waiting,
and/or Timespan as well. In any event, allWaitHandles
have available to them multiple overload forms that can be further viewed in the .NET SDK documentation.//We wait for the default callback
AsyncWS.AsyncWaitHandle.WaitOne();
A quick digression regarding the line above: although our
WaitOne
method as shown in default form will work fine, it can be overloaded. To illustrate, our WaitOne
Handler, once overridden in a derived class, will block the current thread until the current WaitHandle
receives a signal. The two arguments it allows, paralleling what the other two Wait
methods also accept, are:- waiting for a set number of milliseconds to pass, and
- a Boolean value, true or false, specifying whether to exit the synchronization domain before the wait.
Once the wait is over, we return our results via the
EndShowSuppliers
method that got the OK from theWaitHandle
.
The
AsyncWaitHandle
method being a resource releaser, OK’d our supInfo.EndShowSuppliers
method below, and in turn obtained our data, without letting the rest of our application grow impatient.//We return the data
DataSet MyData = supInfo.EndShowSuppliers(AsyncWS);
Therefore,
in coping with the possible speed limitations resulting from HTTP
network traffic, we can implement our Web Service without any concern
that the rest of our application will wait impatiently until it’s all
completed. With some asynchronous"ity,” our application will run its
entire course, in conjunction with our possibly leisurely Web Service,
simply jumping in when it’s finished.
In summing up, our
BeginShowSuppliers
method, once initialized, returns instantly (though not in this case, as we used the WaitHandler
) to notify your applicable callback function that the process is done, and it’s OK to call the EndShowSuppliers
method and return the results.
And
there you have it: a quick look at Asynchronous Web Services. As you
may have wondered, yes, this can get far more complex than what we’ve
discussed. Nevertheless, with the amount you have learned now, it should
be a little less intimidating.
CONCLUSION
Realizing
how fast you can create a nice, functional .NET XML Data Web Service —
and an Asychronous one to boot -รข€“ should have put a smile on your
face.
Additionally, be sure to explore all the .NET documentation for greater detail on what was presented here for additional tweaking, implementing error handling, security, caching, and other aspects you should keep in mind when creating and consuming Web Services. There’s plenty of stuff in the .NET documentation and online that goes into more detail about all this.
Additionally, be sure to explore all the .NET documentation for greater detail on what was presented here for additional tweaking, implementing error handling, security, caching, and other aspects you should keep in mind when creating and consuming Web Services. There’s plenty of stuff in the .NET documentation and online that goes into more detail about all this.
EndInvoke