AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1740&pId=-1
Summarizing AJAX Patterns Under ASP.NET
page
by Xianzhong Zhu
Feedback
Average Rating: 
Views (Total / Last 10 Days): 39534/ 65

Introduction

Ever since early 2005, Ajax techniques have been continuously developing without uniform patterns. Thus, readers may have found out that various Ajax based projects are implemented with numerous differences, especially in terms of details. However, in this article we will start with a relaxing topic; I will summarize several typical Ajax patterns leveraged under the ASP.NET development environment. Of course, in order for you to achieve a detailed comprehension and comparison with each of the patterns, I will also provide related samples.

The XMLHTTP+WebForm Pattern

As you may have remembered, XMLHTTP+WebForm may be the most original Ajax pattern leveraged in the ASP.NET platform. In this mode, developers directly use JavaScript to manipulate the XMLHTTP object sending asynchronous requests to the server side ASP.NET web forms. On the other hand, a Web form is created on the server side which is used to accept and process the XMLHTTP requests, i.e. Ajax (in essence the XMLHTTP object) serves as the fundamental communicating approach between the server side and the client side.

In this section we will write a simple sample. To gain a more intuitive understanding with the XMLHTTP+WebForm pattern, let us draw a sketch map to illustrate the general flow (which is also used in our sample), as is shown in Figure 1.

Figure 1 - The sketch map of the XMLHTTP+WebForm styled implementing flow

The client side

The main client side secrecy is hidden inside the script file ajax.js. Looking more closely at the attached sample, you will find this file contains three functions: talktoServer, which is the main function performing the task of communicating with the server side, newXMLHttpRequest, which will create a cross-browser compatible XMLHTTP object, and getReadyStateHandler, which is the client side callback function of the created XMLHTTP object, responsible to judge the callback states and use the returned data to update the web page. Because in the next section we will continue to use the file ajax.js and delve into these three functions, we will not list their related code here.

The server side

The server side is composed of two pages. The first page is WebForm.aspx which is the main page to render the server-side returned data onto the screen. Figure 2 indicates the design-time snapshot of this page.

Figure 2 - The design-time snapshot of the first example

When the user enters something in the textbox and clicks the "Submit" button, a synchronous invocation will be triggered. And if the request succeeds, the div element at the bottom will be populated with the newly returned data from the server side.

Now, let us look at the HTML code of this page.

Listing 1

<head runat="server">
    <title>Hello Ajax---XmlHttp+Web Form</title>
    <script type="text/javascript" src="js/ajax.js"></script>
 
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div style="text-align: center; width: 420px">
        <h1>XmlHttp+Web Form Pattern</h1>
        <br />
        <input id="testmsg" type="text" value="Hello AJAX" 
           style="width: 214px"/>
        <br />
        <button onclick="talktoServer('ajax.aspx')">
            Submit</button>
        <br />
        <br />
        <div id="msg_display" >
          The server side returned info will be shown here...</div>
    </div>
</body>

Pretty simple, isn't it? The only key point relates to the click event handler of "Submit," which is just the JavaScript function talktoServer discussed above.

The second page is ajax.aspx, which is the asynchronously requested page serving as the server side network gate, accepting the passed string from the XMLHTTP object, further processing the string, and at last returning it to the client side. Below is the related code of this page.

Listing 2

protected void Page_Load(object sender, EventArgs e)
{
  string str = "From the server-side ajax.aspx:"+Request["msg"]+"<br/>Your IP
  address:";
  str += Request.UserHostAddress;
  str += "<br/>The Datatime on the Server side:";
  str += DateTime.Now.ToLocalTime ();
  Response.Write(str);
}

Now, you can press F5 to launch the sample page WebForm.aspx. And with everything alright, you will catch sight of a snapshot, as shown in Figure 3.

Figure 3 - When the user enters something and clicks the "Submit" button a new string will be returned and stuffed into the below div element

The XMLHTTP+HttpHandler Pattern

Now, let us research into another popular pattern leveraged under the ASP.NET platform--the XMLHTTP+HttpHandler pattern.

Introduction

As you have seen, the first pattern, XMLHTTP+WebForm, is simple with clear logics between the components. However, there are also troubles with this pattern: with the project becoming even larger, there are maybe hundreds of thousands of asynchronous requests sent out from the client side pages, and when hundreds of Ajax pages require introduction to respond to the client side acceptances. How will you efficiently maintain such awesome stuff?

To overcome the above how-do-you-do, some readers may put forward the following kind of solution: make all the requests attached to a single page, and then specify different requests with different names of types. To some degree, this does help to abate the above contradiction. Nevertheless, if you have a test with the above case by yourself, you will find out that the application logics will become complicated, which will naturally result in the difficult maintenance. On the other hand, a Web form is, after all, a common page. Although the original idea is to make some computation on the server side, a complete page life cycle will have to be executed. This is apparently a big exhaustion for the server side resource.

So, how can we further improve the above solution? Well, the answer lies right in the HttpHandler.

In the ASP.NET architecture, HttpHandler is one of the core modules to process the HTTP protocol, which, in fact, implements the functionality of an ISAPI Extension extending the means of processing Http Requests and sending related Responses.

Digging further, you will find the functionality of the HttpHandler is accomplished through interface IHttpHandler. Figure 4 illustrates the general processing flow of a HTTP request. With the number order marked in the figure, it is easy to find out the following routine: a Web request first reaches the IIS, and then through aspnet_isapi.dll the request is passed over to the ASP.NET engine. Next, inside the ASP.NET engine the web request will pass through a possible complex tunnel; after being processed by several HTTP Modules, the request at last comes to the HTTP Handler. It is through the HTTP Handler that the processed result again returns back to IIS which bears the responsibility of sending the result back to the client side that sends the request.  Finally, the whole process comes to an end.

Figure 4 - The sketch map of the XMLHTTP+WebForm styled implementing flow

As is shown in Figure 2, at the end of the ASP.NET channel is the HTTP Handler (or Handler factory in the case of multi-handlers). In essence, every ASP.NET Page class derives from the IHttpHandler interface, as is indicated in Figure 5 captured using .NET Reflector.

Figure 5 - Use .NET Reflector to view the Page derivation

Next, let us continue to examine the interface IHttpHandler.

Listing 3

public interface IHttpHandler
{
  // Methods
  void ProcessRequest(HttpContext context);
  // Properties
  bool IsReusable
  {
    get;
  }
}

Here, you can use Response together with Request to work with method ProcessRequest to achieve your custom handler. The property IsReusable is used to specify whether the implementing class for interface IHttpHandler requires buffering.

The HttpHandler Styled Sample

In this section, we will start to create a simple HttpHandler styled sample. First, let us consider how to implement the custom HttpHandler which will be used to process the requested string. The following gives the complete implementation of our custom HttpHandler HelloAjax.

Listing 4

namespace HelloAjax
{
  public   class HelloAjax:IHttpHandler
    {
        #region IHttpHandler member
        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
        public void ProcessRequest(HttpContext context)
        {
           str += context.Request.UserHostAddress;
           str += "<br/>The Datatime on the Server side:";
           str += DateTime.Now.ToLocalTime ();
           context.Response.Write(str);
        }
        #endregion
    }
}

Here, the functionality of method ProcessRequest is simple: just construct a simple string, and then send the string back to the client side.

Author's Note: For the special .ashx type of HttpHandler, we do not need to build up a special .DLL assemble to encapsulate it and then to refer it in the target website. However, for the general custom HttpHandler, you have to set up a .DLL assemble to encapsulate it, modify the <httpHandlers> section in file web.config, and then to refer to it in the target website. Only through the above three steps can you use the custom HttpHandler on the fly; or else, you will fail.

Now, with everything about the custom HttpHandler getting ready, let us shift our attention to the client side related programming. First, look at the HTML code for the related sample page HttpHandlerForm.aspx.

Listing 5

<head runat="server">
    <title>Hello Ajax---XmlHttp+HttpHandler</title>
    <script type="text/javascript" src="js/ajax.js"></script>
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />
 
</head>
<body>
    <div style="text-align:center;width:420px">
        <h1>XmlHttp+HttpHandler Pattern</h1>
    <br />
    <button onclick="talktoServer('HelloAjax.ashx');">Submit</button>
</div>
</body>

I believe that your attention will surely be attracted by the click handler of the "Submit" button, "talktoServer('HelloAjax.ashx');," because there is nothing peculiar with the other stuff. Let us go on with the story to see the crucial JavaScript function.

Listing 6

function talktoServer(url){
  var req = newXMLHttpRequest();
  var callbackHandler = getReadyStateHandler(req);
  req.onreadystatechange = callbackHandler;
  req.open("POST", url, true);
  //encode the url
  req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  //get the string entered into the textbox control
  var testmsg = document.getElementById("testmsg");
  var msg_value = testmsg.value;
  //send the string to the server side
  req.send("msg="+msg_value);
}

Here, we first call another method newXMLHttpRequest() to create a XmlHttp object. Then we set up the client side callback function getReadyStateHandler() and pass as a parameter the newly-created XmlHttp object to it. Next, we attach the callback function getReadyStateHandler() to the onreadystatechange event of the XmlHttp object req. The subsequent several sentences are easy to know since they are the common XmlHttp object related expression. However, there is also one point deserved to be noticed.

Listing 7

req.open("POST", url, true);

Note the "url" above points to "HelloAjax.ashx," not a general .txt/.xml/.html/.asp/.aspx/.php/.jsp file. Then by executing the server side method ProcessRequest of the HttpHandler "HelloAjax.HelloAjax," the specified string returns to the client side.

For brevity, we omitted the other two functions (newXMLHttpRequest and getReadyStateHandler). You can refer to the downloadable source code for details.

Now, when everything gets ready, you can press F5 to launch the above sample page. And with everything smooth, you will get a snapshot most like that in Figure 6.

Figure 6 - The server-side string is returned when you click "Submit"

In contrast to the XMLHTTP+WebForm pattern in the first part, the HttpHandler way is more light-weighted, helping to release the payload of the whole system, but still trivial and annoying.

Next comes another Ajax pattern under the ASP.NET environment - the ASP.NET 2.0/3.5 callback function pattern.

The ASP.NET 2.0/3.5 Callback Function Pattern

Introduction

Many people said that it is a pity ASP.NET 2.0 went to the market a minute earlier than Microsoft ASP.NET AJAX 1.0 framework. However, from my point of view, this standpoint is half right, because ASP.NET 2.0 has also built in the support for Ajax, i.e. Callback, whose functionalities, to be honest, are not powerful enough, and whose utilization is not easy to follow.

Anyhow, the Callback function pattern is representative of Microsoft's earliest Ajax policy, with the target of enabling users to use it as its popular PostBack so as to asynchronously update part of a web page. However, with further digging into it, many developers have found out that it is rather difficult to extend the Callback function, which naturally results in the limitation of usage.

As is well known, to put Callback into usage, a Page has to implement interface ICallbackEventHandler which provides two methods, i.e. GetCallbackEventReference and RaiseCallbackEvent. The former is used to get the name of a client-side function that will be called to initiate a callback. Since the GetCallbackEventReference method is declared in the ICallbackEventHandler namespace, your server-side Page must explicitly implement this interface; and accordingly, your client-side JavaScript has to explicitly call the ClientScript.GetCallbackEventReference() method. The latter performs the task of processing and returning the callback. For a clearer explanation of the ASP.NET 2.0 Callback mechanism, you can refer to my previous article published at ASPAlliance.

In the next section, we are to create a simple sample to learn ASP.NET 2.0's script Callback support, through which we are to investigate how the Ajax styled partially update is accomplished.

The Callback Styled Sample

Note that this sample corresponds to an individual website named CallbackForm.

Next, let us first take a look at the server side coding (in fact implemented in the associated CodeFile .aspx.cs). As is emphasized above, to trigger the Ajax functionality, we have to explicitly derive the Page from interface ICallbackEventHandler, as is indicated below.

Listing 7

public partial class _Default : System.Web.UI.Page ,ICallbackEventHandler 
{
    //define a global string to be used by the callback
    string str = "";
    //define the method invoked when callbacking the server
    public void RaiseCallbackEvent(String eventArgument)
    {
        str += Request.UserHostAddress;
        str += "<br/>The Datatime on the Server side:";
        str += DateTime.Now.ToLocalTime();
    }
    // specify the string returned to the CallBack
    public string GetCallbackResult()
    {
        return str;
    }
}

As you've seen herein, the main task is undertaken by method RaiseCallbackEvent. In this case, it only returns a simple string holding the client-side IP address and the server-side datetime info. As for another method GetCallbackResult, it directly posts the string produced in its related method RaiseCallbackEvent back to the client side. Note in real scenarios, you may populate whatever complex stuff you want to in method GetCallbackResult.

Next, let us check out the related client side implementation. With the help of ASP.NET 2.0/3.5 Callback function support, in this sample, file ajax.js (which is used in the previous sample) is no longer referred to, i.e. without the need to manipulate the XMLHTTP object directly. However, another two client side functions, CallTheServer and ReceiveServerData, are present before us, with which the client side Ajax related programming gets further simplified. The following gives the key pieces of code concerning the client side programming.

Listing 8

……
    function ReceiveServerData(arg,context)
    {
        document.getElementById("msg_display").innerHTML=arg;
    }
    function CallTheServer(arg, context) {
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <h1>The ASP.NET 2.0/3.5 Callback Function Styled Sample</h1>
    <div>
        <input id="Text1" type="text" />
        <input id="Button1" type="button" value="Submit"
  onclick="CallTheServer(document.getElementById('Text1').value)" />
    </div>
    </form>
</body>

As mentioned above, to trigger a call back to the server side, the client side should first fire a call back invocation. The function CallTheServer here shows a typical usage. In fact, as you may have found out, the true secrets lies in the function ClientScript.GetCallbackEventReference(…). Note the ClientScript property of the Page object just equals the ClientScriptManager class, which is used, by default, to manage client scripts and add them to Web applications. There are several versions of method GetCallbackEventReference. For more researching into this, I recommend you to use .NET Reflector to give it a thorough examination. Here, we are only interested in the parameters utilized above.

(1). this: Means the server control that executes the call back is the current Page. Moreover, the current Page has to implement interface ICallbackEventHandler. In other cases, this parameter may point to a Web control (in this case, it must also implement interface ICallbackEventHandler).

(2). arg: Corresponds the argument eventArgument that is passed to the server side function RaiseCallbackEvent. You can use a custom string for this parameter.

(3). ReceiveServerData: This is the name of the client side script function used to process the returned data after the successful invocation of the call back. Note this function must exist inside the page that executes the call back. It can contain two arguments, the first one relates to the returned data of the call back, while the second one corresponds to the context returned after the call back, which are all strings.

(4). context: This cannot be changed to be passed to the function processing the returned data.

As for function ReceiveServerData, things become simple. It used the returned data (the first argument) to populate the target HTML element.

Purely examined from this simple sample, you may not find out the simplifications, while in real complex cases it does.

Finally, let us take a look at the running time snapshot, as shown in Figure 7.

Figure 7 - When the user enters something and clicks "Submit," the below div element will be populated with data returned from the server side

The Ajax Framework Pattern

To radically overcome the Ajax related bothering, numerous Ajax frameworks emerge, like bamboo shoots after a spring rain. On the whole, all the Ajax frameworks can be divided into three categories of frameworks: some rely upon the relevant server side techniques, some are based on the client side JavaScript + DOM stuffs and the rest piece the former both together. For various reasons, we herein only touch upon two of the above frameworks: Microsoft ASP.NET AJAX Framework, and a new client-side Ajax framework jQuery.

(1) Using Microsoft ASP.NET AJAX Framework

As for Microsoft ASP.NET AJAX framework, there are a lot stories with it; in this article we are not dwelling upon it, but are going to run directly into the topic. Note in this section we will still bring you a simple sample for the purpose of comparison with the other solutions.

Digging more into the ASP.NET AJAX framework, you will find there are several means to communicate with the server side data. However, the typical mode to consume the server-side data is via Web Services. Below only gives a typical and rough description of the process calling Web Service from JavaScript, as is illustrated in Figure 8.

Figure 8 - The typical process to call Web Service from inside the client side of ASP.NET AJAX framework

The subsequent Figure 9 gives the initial running time snapshot of the sample in this section.

Figure 9

When the user populates something and clicks "Submit," its related click event handler will trigger the Ajax styled invocation of the local Web Service (to be discussed in a minute) passing the entered string. Then, the Web Service method will generate a corresponding short message and return it back to the client side. Finally, if the invocation (in Ajax style) succeeds, the returned data will be rendered on the browser side screen, as shown in Figure 10.

Figure 10 - The server-side message is returned asynchronously and rendered on the screen

Next, let us start the detailed programming from the beginning of writing the Web Service.

Writing the Web Service

Right click the sample website and select "Add new items…" to add a new Web Service "WebService.asmx" to the project. In this case, we will create a simple web method named "HelloWorld."  The following gives the related code.

Listing 9

……
[ScriptService]
public class WebService : System.Web.Services.WebService {
……
[WebMethod]
public string HelloWorld(string instr) {
    string str = "From the server-side:" + instr + "<br/>Your IP address:";
    str += System.Web.HttpContext.Current.Request.UserHostAddress;//ip
    str += "<br/>The Datatime on the Server side:";
    str += DateTime.Now.ToLocalTime();//
    return str ;
}

Apparently, method HelloWorld is a common web method without anything peculiar. The point worthy to be noticed is that we have to put an attribute [ScriptService] ahead of the WebService class so as to let ASP.NET AJAX generate for us the client side asynchronous Web Service proxy and further allow us to call the web method directly. As for the web method HelloWorld itself, it simply pieces together several strings concerning the server-side info and then returns it.

Invoking the Web Service from the .aspx Page

First of all, dragging a ScriptManager control onto the sample .aspx page (Default.aspx in our case) is a MUST HAVE whether to write ASP.NET AJAX Server Centric or Client Centric based applications. (NOTE: In Visual Studio 2008, you can directly add an "Ajax Web Form" to simplify this step.) After this, we should also add reference to the Web Service defined above inside the ScriptManager control, so that the ASP.NET AJAX framework can automatically generate the client side asynchronous invocation proxy.

Listing 10

<asp:ScriptManager ID="ScriptManager1" runat="server">
  <Services>
    <asp:ServiceReference Path="~/WebService" />
  </Services>
</asp:ScriptManager>

Next, let us examine how the JavaScript invocation is programmed.

Listing 11

……
<script type="text/javascript">
    function OnShow(result)
    {
        var s = $get("msg_display");
        s.innerHTML = result.toString();
    }
  function SayHello()//call the server-side web service
    {
        var fs = WebService;
        //invoking the web method
        fs.HelloWorld($get("testmsg").value,OnShow);
        return false;
    }
</script>
……
<br />
&nbsp;<br />
<br />
<div id="msg_display">
 The server side returned info will be shown here...</div>

Please note the invoking format for the web method SayHello, which is the most important line of code for now because it means, herein, we use the JavaScript in a very easy-to-follow mode to call the client-side proxy generated for the Web Service HelloWorld automatically by the Microsoft Ajax asynchronous communication layer.

Next, method OnShow corresponds to the client side callback function called if the Web Service invocation succeeds. In it, we assigned the returned value from the server side to the innerHTML property of the HTML <div/> element "msg_display." And lastly, if everything goes smoothly, you will see the running time result in Figure 7 and 8.

Here, we have depicted the ASP.NET AJAX pattern in a hurry. As for more concerning the Microsoft Asynchronous Communication Layer and the associated web service proxy, you can refer to the online documents at the official website (http://www.asp.net/ajax/documentation/live).

Next, let us switch our topic to another currently hot JavaScript framework jQuery, by which we can also achieve the aim of returning the server-side data asynchronously.

(2)Using jQuery Framework

As a relative latecomer to this world of client-side Ajax libraries, jQuery has taken the web development community by storm, quickly winning the support of major websites. Compared with other JavaScript frameworks, jQuery aims to help designers to leverage their existing knowledge of CSS, XHTML, and good old straightforward JavaScript to manipulate page elements directly, so that they do not need to spend plenty of time juggling the complexities of advanced JavaScript.

Far more than mentioned above, jQuery has also provided the popular Ajax support with concise syntax and rich contents, as well as extensible plug-in support. For brevity, we will not use more words to introduce jQuery. So, for a thorough study and detailed references, you can check out the appended URL's in this article.

Sample 1

Next, let' us look at some Ajax related examples using jQuery. Although the jQuery framework has provided several means to get data from the server side in the asynchronous mode, the simplest way may be using command $.get, whose complete syntax is as follows:

$.get(url,parameters,callback)

This command will initiates a GET request to the server using the specified URL with any passed parameters as the query string.

Here, we still skip the detailed explanation, but focus upon the example itself. Below gives the HTML code that uses the $.get method.

Listing 12

<script type="text/javascript" src="jquery-1.2.6.js"></script>
<script type="text/javascript">
$(function() {
      //this code is executed when the page's onload event fires
          $('#Button1').click(function() {
                $.get("ajax.aspx", function(response) {
                      $('#result_panel').html(response).show();
                });
          });
    });
</script>
…………
<input id="Text1" type="text" value="Hi,ajax" />
<input id="Button1" type="button" value="Talk to Server 1"/>
<div id="result_panel" ></div>

First of all, we should add reference to the core file of jQuery--jquery-1.2.6.js (the newest version at the time of this writing) at the .aspx file. Then we attach a click event handler to button "Button1," and inside the event function we called the jQuery method $.get. Here, we omit the second parameter of method $.get. In the third parameter (i.e. the callback) we used the jQuery special selector to locate the <div/> element"'result_panel," set its content using the returned value, and finally show it (using the show method). As for the first parameter of method $.get, "ajax.aspx," it is quite similar to the one discussed in the first XMLHTTP+WebForm pattern in this article.

Now you can press F5 to start this sample page (jQueryMode.aspx). With everything smooth and after pressing the button "talk to Server 1," you will get the snapshot as shown in the top part of Figure 11.

Figure 11 - The running time snapshot using jQuery's $.get method

However, to gain the full control of an Ajax request to the server side, you may have to rest upon the $.ajax() method provided by jQuery.

Sample 2

Let us first look at the general syntax of method $.ajax().

$.ajax(options)

This will initiates an Ajax request using the passed options to control how the request is made and callbacks notified. Note the parameter options are of type Object which in fact contains more than ten properties.

Let us still skip the details and see a related sample application. The following gives the key HTML code.

Listing 13

function Button2_onclick() {

        $("#News").html("Loading News......");

        $.ajax({

            type:"post",

            url:"ajax.aspx?msg=" +"Hi",

            dataType:"html",

            data:"",

            timeout: 2000,

            error: function(){

                alert('Error loading HTML document');

            },

            success:function(result)

            {

                $("#News").html(result).show();

            }

        });

}

…………

<div id='News' style="border-style: outset; border-color:
 #800000"></div>

<input id="Button2" type="button" value="Talk to Server 2"
  onclick="return Button2_onclick();return false;" />

Here, function Button2_onclick contains two sentences: the first is easy to see (which sets the content of the element "News"), the second corresponds to method $.ajax(), which holds several arguments. Here, property type specifies the mode of invoking the HTTP protocol; the url gives the url and related parameters for the request; dataType identifies the type of data that is expected to be returned by the response. The data serves as the query parameters to be passed to the request (If the request is a GET, this data is passed as the query string. If a POST, the data is passed as the request body.). Timeout specifies a timeout for the Ajax request in milliseconds, error is a function invoked if the response to the request returns an error status code and success is a function invoked if the response to the request indicates a success status code.

Now you can launch the sample page to give it a test. The related snapshot is given at the bottom part in Figure 9.

In fact, jQuery provides more powerful and complete Ajax functionalities than the above two samples can hold; however, the more interesting things should be excavated by you, dear readers.

Downloads
References
Summary

In this article we have summarized the typical Ajax patterns utilized under the ASP.NET environment. By looking through and comparing each of the patterns introduced above, you are sure to find out what you really need in your practice.

There is no doubt that, with the gradual maturity of various existing Ajax framework, people have become accustomed to opting for the fourth pattern (the existing Ajax framework) through which they will not worry about the differences between various browsers and DOM-related stuff any longer. Due to our main interests lying in distinguishing the different patterns, the sample applications are pretty simple (sorry for this). However, I am sure the demos do show you the philosophies of each solution.



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-25 6:51:01 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search