13.3. Ajax.NET Professional

Ajax.NET Professional (available at www.ajaxpro.info) is an Ajax framework designed for use with the .NET framework, specifically ASP.NET. As with JPSpan and DWR, Ajax.NET allows developers to create classes on the server that can be accessed via the client. This is done by once again abstracting out the communication between client and server, providing cross-browser communication behind the scenes.

This framework uses the .NET version of reflection to inspect the classes and create a JavaScript interface. Ajax.NET Professional also takes advantage of a .NET feature called attributes, which are used to mark specific methods that should be available to the client. This allows developers to concentrate on the true object-oriented design without worrying about exposing sensitive methods to the client.

13.3.1. Using Ajax.NET Professional

To use Ajax.NET Professional, you need to be running IIS version 5 or greater and have the .NET Framework installed. Download the package of DLLs from the web site. There are two sets of DLLs, one for use with .NET 1.1 and one for .NET 2.0. If you already have an ASP.NET project (Visual Studio .NET) or a web site (Visual Studio Web Developer Express or Visual Studio 2005), you can use it with Ajax.NET Professional by referencing the appropriate DLLs. If you are using .NET 1.1, add a reference to AjaxPro.dll to your project; for .NET 2.0, add a reference to AjaxPro.2.dll. This should be done for every project/web site that needs to reference Ajax.NET Professional.

Next, open up web.config and add the following inside of the <system.web/> element:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <httpHandlers>
      <add verb="POST,GET" path="ajaxpro/*.ashx"
           type="AjaxPro.AjaxHandlerFactory, AjaxPro.2"/>
    </httpHandlers>
    <!-- more code here -->
  </system.web>
</configuration>

These lines create a new HTTP handler for the web application. For all POST and GET requests to the server that come through the path ajaxpro and end with an extension of .ashx, the AjaxPro.AjaxHandlerFactory will be used instead of the default handler (this has no effect on other requests coming into the server).

13.3.1.1. Marking Methods

Ajax.NET Professional expects the developer to mark any methods that should be available on the client with the AjaxPro.AjaxMethod attribute. This should be placed just before the method definition in the source code. For example, consider this simple C# class:

namespace AjaxNET.Example {
    public class ExampleClass {

        [AjaxPro.AjaxMethod]
        public String GetMessage() {
            return "Hello world!";
        }
    }
}

The highlighted line is the attribute necessary for publishing the GetMessage() method for client-side use. There is one additional step necessary for generating the JavaScript wrappers.

When the page that needs to use this class is instantiated, it must indicate which classes should be available to the client. This is done by calling AjaxPro.Utility.RegisterTypeForAjax() and passing in the type of class to marshal, such as:

AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxNET.Example.ExampleClass));

This call should take place in the Page_Load method of an ASP.NET page to ensure that the class will be available when called and also includes all of the necessary JavaScript resources.

NOTE

Make sure you have at least one <form runat="server"> element in your page, as Ajax.NET Professional uses this to insert the JavaScript files.

13.3.1.2. Using the JavaScript Wrappers

The JavaScript wrappers generated for .NET classes function similarly to those created by DWR; each method of an object becomes a static method of a corresponding JavaScript. Arguments are passed in as if it was a normal JavaScript function, and the last argument is a callback function to handle the server response.

Callback functions in Ajax.NET Professional receive a response object as their only argument. This object contains three properties: error, which is set to true if there was a communication error; request, which contains the XMLHttp object used to make the request; and value, which is the return value of the method call. For example, the GetMessage() method in the previous example can be called like this:

function handleResponse(oResponse) {
    if (!oResponse.error) {
        alert(oResponse.value);
    }
}
AjaxNET.Example.ExampleClass.GetMessage(handleResponse);

In most cases, it will be necessary to look only at the response object's error and value properties, as in this example. This creates an asynchronous request to the server, ultimately executing the appropriate method. However, Ajax.NET Professional also offers the ability to make a call synchronously.

To create a synchronous call to the server, you need only omit the callback function when calling the method. If the last argument isn't a function, Ajax.NET Professional assumes that the call should be made synchronously and instead of using a callback function it returns the response object as the function value:

var oResponse = AjaxNET.Example.ExampleClass.GetMessage();
if (!oResponse.error) {
    alert(oResponse.value);
}

The response object returned from a synchronous call is the exact same object that would have been returned from an asynchronous call. Once again, it's best to check for errors before using the value that was returned.

NOTE

As mentioned throughout the book, synchronous calls should be used sparingly if at all. Every synchronous requests locks the user interface while waiting for a response, so it could drastically effect the experience of users should a call take longer than usual to return.

13.3.2. Type Conversion

By default, Ajax.NET Professional handles the seamless conversion of JavaScript data types and objects to .NET and back. Additionally, there is some built-in support for common .NET complex types, such as an ADO.NET DataSet. Ajax.NET Professional creates appropriate JavaScript object definitions. When it provides such an interface, the same properties and methods are available on the client as there on the server, the only difference being that it is completely read-only.

It's also possible to return a custom class to JavaScript. To do so, it must be marked with the Serializable attribute so that Ajax.NET Professional can convert it into a JSON format. For example, consider a class that represents a web site user:

[Serializable()]
public class User
{
  private string _userName = "";
  private string _userEmail = "";

  public string UserName

{
    get
    {
      return _userName;
    }
  }

  public string UserEmail
  {
    get
    {
      return _userEmail;
    }
  }
  public User(string Name, string Email)
  {
    _userName = Name;
    _userEmail = Email;
  }
}

Assuming that a User object was returned by a method named GetUserFromId(), the properties would be available in JavaScript, such as:

MyClass.GetUserFromId(6, callback);

function callback(oResponse)
{
  var oUser = oResponse.value;
  alert(oUser.UserName + "
" + oUser.UserEmail);
}

13.3.3. Session Access

Ajax.NET Professional can also allow access to objects in the session scope. This is accomplished by passing a parameter to the AjaxPro.AjaxMethod() attribute specifying the level of access that is needed. The access level is specified by an enumeration called HttpSessionStateRequirement, which has three values: Read, Write, and ReadWrite. This type of access is established like this:

[AjaxPro.AjaxMethod(HttpSessionStateRequirement.Read)]
public string MyMethod()
{
    //code here
}

This code specifies that MyMethod() should have read access to variables that are in the session scope when called from the client. Without specifying this argument to the AjaxPro.AjaxMethod() attribute, the method would be unable to access HttpContext.Current.Session when called using JavaScript.

NOTE

When specifying access to session-level variables, it's best to go with the lowest permission level necessary to complete the task. For instance, don't specify ReadWrite if you only need Read.

13.3.4. Ajax.NET Professional Example

Once again, this example focuses on retrieving customer information from the server. However, since .NET applications typically use SQL Server, this example assumes that the same Customers table used in the previous examples exists in a SQL Server database.

A SQL script for creating the Customers table in SQL Server is included in the code example download at www.wrox.com.

This example requires a web site (.NET 2.0); you can either create a new web site or add on to an existing one. Either way, your first step is to modify the web.config file as specified earlier in this section. The next step after that is to begin coding the CustomerInfo class.

13.3.4.1. The CustomerInfo Class

The CustomerInfo class in .NET is very similar to the one used for DWR and Java. The basic makeup of the class is once again an object with a single method called GetCustomerInfo() that accepts an integer ID and returns a string of HTML formatted code:

using System.Data.SqlClient;

namespace Wrox {

    public class CustomerInfo {

        [AjaxPro.AjaxMethod()]
        public string GetCustomerInfo(int id) {
            //more code here
        }

    }
}

This class must include the System.Data.SqlClient namespace, which contains the objects necessary for connecting to a database. Before initiating a database connection, however, several variables must be defined containing information about the database to connect to:

using System.Data.SqlClient;

namespace Wrox {

    public class CustomerInfo {

        [AjaxPro.AjaxMethod()]
        public string GetCustomerInfo(int id) {

            string info = "";
            string dataSourceName = @"localhostSQLEXPRESS";
            string catalogName = "ProAjax";
            string connectString = String.Format(
                   "Data Source={0};Integrated Security=SSPI;Initial Catalog={1}",
                   dataSourceName, catalogName);

string query = String.Format(
                   "Select * from Customers where CustomerId={0}", id);

            SqlConnection conn = null;
            SqlCommand command = null;

            //more code here


            return info;
        }

    }
}

The very first variable defined, info, is the string that holds either the customer information string or an error message to return to the client. The next few string variables define the information necessary for connecting to a SQL Server. These variables have default values here that assume you have a version of SQL Server Express running on the same server as IIS; these settings should be changed to appropriate values for your server configuration. The last two variables are conn, which is a database connection, and command, which is the method by which SQL commands are executed. Next, the database connection must be created, and the query must be run:

using System.Data.SqlClient;

namespace Wrox {

    public class CustomerInfo {

        [AjaxPro.AjaxMethod()]
        public string GetCustomerInfo(int id) {

            string info = "";
            string dataSourceName = @"localhostSQLEXPRESS";
            string catalogName = "ProAjax";
            string connectString = String.Format(
                   "Data Source={0};Integrated Security=SSPI;Initial Catalog={1}",
                   dataSourceName, catalogName);
            string query = String.Format(
                   "Select * from Customers where CustomerId={0}", id);

            SqlConnection conn = null;
            SqlCommand command = null;


            try
            {
                conn = new SqlConnection(connectString);
                command = new SqlCommand(query, conn);

                conn.Open();
                SqlDataReader reader = command.ExecuteReader();

                //more code here

conn.Close();
            }
            catch (Exception ex)
            {
                info = "Error occurred while trying to connect to database: "
                          + ex.Message;
            }

            return info;
        }

    }
}

As it is with Java, it's important to wrap database operations inside of a try...catch block because there are numerous places where an error may occur. The first new step here is to create an instance of SqlConnection by passing in the previously defined connection string. Then, a new SqlCommand object is created by using query and the newly created connection; this is the interface by which the query can be executed. With the objects defined, it's time to open the connection using the Open() method. Now the query can be run by using the command variable, and more specifically, the ExecuteReader() method, which creates and returns a SqlDataReader object. If an error occurs at any time during this code, it is caught and the info variable is filled with an error message returned to the client.

The last step is to add in the code that reads from the database and formats an HTML string:

using System.Data.SqlClient;

namespace Wrox {

    public class CustomerInfo {

        [AjaxPro.AjaxMethod()]
        public string GetCustomerInfo(int id) {

            string info = "";
            string dataSourceName = @"localhostSQLEXPRESS";
            string catalogName = "ProAjax";
            string connectString = String.Format(
                   "Data Source={0};Integrated Security=SSPI;Initial Catalog={1}",
                   dataSourceName, catalogName);
            string query = String.Format(
                   "Select * from Customers where CustomerId={0}", id);

            SqlConnection conn = null;
            SqlCommand command = null;

            try
            {
                conn = new SqlConnection(connectString);
                command = new SqlCommand(query, conn);

                conn.Open();
                SqlDataReader reader = command.ExecuteReader();

if (reader.HasRows)
                {
                    reader.Read();
                    info = String.Format("{0}<br />{1}<br />{2}<br />{3}<br
/>{4}<br /><br />Phone: {5}<br /><a href="mailto:{6}">{6}</a>",
                        reader.GetString(reader.GetOrdinal("Name")),
                        reader.GetString(reader.GetOrdinal("Address")),
                        reader.GetString(reader.GetOrdinal("City")),
                        reader.GetString(reader.GetOrdinal("State")),
                        reader.GetString(reader.GetOrdinal("Zip")),
                        reader.GetString(reader.GetOrdinal("Phone")),
                        reader.GetString(reader.GetOrdinal("Email"))
                    );
                }
                else
                {
                    info = String.Format("Customer with ID {0} doesn't exist.",
                                         id);
                }

                conn.Close();
            }
            catch (Exception ex)
            {
                info = "Error occurred while trying to connect to database: "
                          + ex.Message;
            }

            return info;
        }

    }
}

After the query has been executed, calling reader.hasRows() determines if there are any results. If so, then reader.Read() moves the reader to the first (and in this case, only) matching row. Then, the string is constructed getting using String.Format() to insert information into the correct location. If, on the other hand, there is no matching row, info gets filled with a message indicating that the customer doesn't exist. Then you're ready to move to the next step, creating the client page.

NOTE

If you are using .NET 2.0, this class file should be contained within the special App_Code directory of the web site.

13.3.4.2. Creating the Client Page

The client page is a new ASPX file with a codebehind file. This example assumes C# as the server-side language, but it's also possible to create the same functionality using any other .NET language. The ASPX file is fairly simple:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AjaxNETExample.aspx.cs"
    Inherits="AjaxNETExample" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Ajax.NET Professional Example</title>
    <script type="text/javascript"src="AjaxNETExample.js"></script>
</head>
<body>
    <form runat="server"></form>
    <p>Enter customer ID number to retrieve information:</p>
    <p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
    <p><input type="button" value="Get Customer Info"
              onclick="requestCustomerInfo()" /></p>
    <div id="divCustomerInfo"></div>
</body>
</html>

The highlighted lines of code in this example are the only changes from the previous iteration of this example. The codebehind file has most of the important code:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class AjaxNETExample : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        AjaxPro.Utility.RegisterTypeForAjax(typeof(Wrox.CustomerInfo), this);
    }
}

The most important line in the codebehind is the call to AjaxPro.Utility.RegisterTypeForAjax(), which signals that JavaScript wrappers should be created for the Wrox.CustomerInfo class. That's all that is necessary to generate the client-side code. To use the generated JavaScript wrappers, the front end code looks very similar to that of the DWR example:

function handleGetCustomerInfoResponse(oResponse) {
    if (!oResponse.error) {
        displayCustomerInfo(oResponse.value);
    } else {
        alert("An error occurred.");
    }
}

function requestCustomerInfo() {
    var sId = document.getElementById("txtCustomerId").value;
    Wrox.CustomerInfo.GetCustomerInfo(parseInt(sId),
        handleGetCustomerInfoResponse);
}

function displayCustomerInfo(sText) {
    var divCustomerInfo = document.getElementById("divCustomerInfo");
    divCustomerInfo.innerHTML = sText;
}

And that's all it takes to recreate this example using Ajax.NET Professional. Note that the GetCustomerInfo() method is specified in .NET-style Pascal case. This is something to be careful of when using C# objects in JavaScript. Also note that the full namespace of the class is used on the client side (Wrox.CustomerInfo instead of just CustomerInfo).

13.3.5. Summary of Ajax.NET Professional

If you are using ASP.NET to run a web site or web application, Ajax.NET Professional provides very powerful remote scripting capabilities with very little setup. The author, Michael Schwarz, seems to have taken into account most of the needs of Ajax applications in creating this framework. The data type conversion for Ajax.NET Professional is very strong, and it can handle almost any type of custom class, not to mention enumerations. As far as downsides go, Ajax.NET Professional is lacking in documentation, so it can be a bit challenging to work through its quirks in the beginning. There is, however, an active e-mail support list available at http://groups.google.com/group/ajaxpro. Also, Ajax.NET Professional has been tested only on Internet Explorer and Firefox, so developers looking for Safari or Opera support may have to do a bit of testing on their own.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset