Sunday, 1 January 2012

User Interaction between server executions in ASP.NET


Background

When working in ASP.NET, we will be facing requirement to have communication between server and client frequently. For Example, let us consider the below use case.

The requirement is to have a Web page for Customer invoicing. When the user rising invoice, the application should verify as defined below.
  1. Get the Customer ID and Invoice Amount from Web Page
  2. When the user press Save button, the application required to get the existing outstanding amount and allowed outstanding amount for that customer.
  3. When invoice amount + existing outstanding amount is more then allowed outstanding amount then inform the user the amount is exceeded with Proceed and Cancel button.
    1. When the user press Proceed button, it should call the server event for invoicing.
    2. When the user press Cancel button, it won’t proceed.
  4. When invoice amount + existing outstanding amount is lesser then allowed outstanding amount then call the server event for invoicing.
Here, the user press Save button to save the invoice. For that the system will call the server for verifying the outstanding amount is exceeding. If not, it will call continue the process to save the invoice.

If the outstanding amount is exceeding, it will get the user confirmation to continue further process. When user confirm the proceed signal it will proceed as usual and if not it won’t.

The below diagram will be useful for understanding the same –


Implementation

For keeping customer details, I have defined two XML file in the project.
  1. CustomerTypes.xml
    The Customer type xml file holds information about customer types. Currently I have three types such as A, B, C. Each customer type have different allowable outstanding amount.
    <?xml version="1.0" encoding="utf-8" ?>
    <CustomerTypes>
      <CustomerType>
        <Id>A</Id>
        <AllowedOutstadingAmount>25000</AllowedOutstadingAmount>
      </CustomerType>
      <CustomerType>
        <Id>B</Id>
        <AllowedOutstadingAmount>15000</AllowedOutstadingAmount>
      </CustomerType>
      <CustomerType>
        <Id>C</Id>
        <AllowedOutstadingAmount>12000</AllowedOutstadingAmount>
      </CustomerType>
    </CustomerTypes>
  2. Customers.xml
    The Customer xml holds information about customer. Each customer has properties such as Id, Name, Customer Type, Current outstanding amount and Customer Type.
    <?xml version="1.0" encoding="utf-8" ?>
    <Customers>
      <Customer>
        <Id>ALFKI</Id>
        <Name>Alfreds Futterkiste</Name>
        <Phone>030-0074321</Phone>
        <OutstadingAmount>23000</OutstadingAmount>
        <CustomerType>A</CustomerType>
      </Customer>
      <Customer>
        <Id>ANATR</Id>
        <Name>Ana Trujillo Emparedados y helados</Name>
        <Phone>(5) 555-4729</Phone>
        <OutstadingAmount>15000</OutstadingAmount>
        <CustomerType>B</CustomerType>
      </Customer>
      <Customer>
        <Id>ANTON</Id>
        <Name>Antonio Moreno Taquería</Name>
        <Phone>(5) 555-3932</Phone>
        <OutstadingAmount>10000</OutstadingAmount>
        <CustomerType>C</CustomerType>
      </Customer>
    </Customers>
As defined in the xml data, to know the allowed outstanding amount for a customer it must be linked with the CustomerTypes.xml using CustomerType of the customer. The CustomerTypes.xml has the allowed outstanding amount for each customer type. The Customers.xml contains the current outstanding amount, so by adding invoice amount entered in the screen + current outstanding amount it must be lesser then the allowed outstanding amount for processing without user confirmation.

Below is the aspx script for defining user interface (Default.aspx).
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <%--This div control can also place in Master Page, so it can be used for all pages--%>
    <div id="divHidden">
    </div>
    <div id="progrssShow">
        <p style="text-align:center;font-size:20px;color:Red;font-weight:bold;">Please wait.</p>
        <p style="text-align:center;font-size:15px;">Request is being created.... </p>
    </div>
    <div id="confirmShow">
        <p id="Message"></p>
        <div style="text-align:center">
            <input id="Proceed" value="Proceed" type="button" class="Proceed" />
            <input id="Cancel" value="Cancel" type="button" class="Cancel" />
        </div>
    </div>
    <table>
        <tr><td colspan="2">New Invoice</td></tr>
        <tr>
            <td>Customer ID</td>
            <td><asp:TextBox runat="server" ID="txtCustomerID" CssClass="CustomerID" Width="120px" Text=""></asp:TextBox></td>
        </tr>
        <tr>
            <td>Invoice Amount</td>
            <td><asp:TextBox runat="server" ID="txtInvoiceAmount" CssClass="InvoiceAmount" Width="80px" Text=""></asp:TextBox></td>
        </tr>
        <tr>
            <td colspan="2" style="text-align:center">
                <asp:Button ID="btnServerSave" CssClass="btnServerSave" Text="Server Save" runat="server" onclick="btnServerSave_Click" />
                <input id="btnSave2" class="btnSave" value="Save" type="button" />
            </td>
        </tr>
    </table>
    <asp:Label runat="server" ID="lblMessage" Text=""></asp:Label>
</asp:Content>

The User Interface at initial stage will be as bellow


There are three additional div controls are used in the aspx script for giving better usability.

1. divHidden
When pressing Save button, the divHidden div control is used to make the page inactive (the control on the screen will not be usable). The style sheet for the divHidden control defined an image as background and z-index to top.

2. progrssShow
This control is used to show information to the user "Please Wait. Request is being created...." when the application verifying the outstanding amount exceeded. (I use Thread.Sleep(1000) to see the information, if required it can be removed)
The screen layout will be

3. confirmShow
This is again a popup control for getting confirmation from the user to proceed or not. This control also will show the current and allowed outstanding amount.
The screen layout will be

The code behind Default.aspx.cs
protected void btnServerSave_Click(object sender, EventArgs e)
{
    lblMessage.Text = "Server Event called";
}
I have the added below Style Sheet
/* Added Newly */
#divHidden
{
 width: 100%;
 height:100%;
 background-color:Gray;
 background: url(../Images/hidden.png) repeat;
 position: absolute;
 top: 0;
 left: 0;
 z-index: 100;
 display: none;
}
#progrssShow
{
 width: 460px;
 height: 120px;
 background-color: White;
 position: absolute;
 left: 45%;
 top: 45%;
 margin-top: -50px;
 margin-left: -168px;
 vertical-align:middle;
 z-index: 1001;
 display: none;
}
.btnServerSave
{
    display:none;
}
#confirmShow
{
 width: 550px;
 height: 220px;
 background-color: White;
 position: absolute;
 left: 40%;
 top: 40%;
 margin-top: -50px;
 margin-left: -168px;
 vertical-align:middle;
 z-index: 1001;
 display: none;
}
#progrssShow P, #confirmShow P
{
 width: 85%;
 margin: 10px auto;
 text-align: left;
}
#progrssShow P.button #confirmShow P.button
{
 padding-top: 10px;
 border-top: 1px solid #D1D1D1;
}
I am using a Generic Handler for getting customer information such as current outstanding amount, allowed outstanding amount.
public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    string retValue = string.Empty;

    try
    {
        // For delaying the process
        System.Threading.Thread.Sleep(1000);

        if (context.Request.Form["CustomerID"] != null)
        {
            // Get the Customer ID
            string strCustomerID = context.Request.Form["CustomerID"];

            // Get the Customer
            string strCustomerFileName = context.Request.PhysicalApplicationPath + @"Data\Customers.xml";

            if (File.Exists(strCustomerFileName))
            {
                double dblOutstandingAmount = 0;
                string strCustomerType = string.Empty;
                double dblAllowedOutstandingAmount = 0;

                // Getting the existing outstanding amount and Customer Type
                XPathDocument doc = new XPathDocument(strCustomerFileName);
                XPathNavigator nav = doc.CreateNavigator();

                XPathNodeIterator iterator = nav.Select("//Customer[Id = '" + strCustomerID + "']");

                while (iterator.MoveNext())
                {
                    XPathNavigator nav2 = iterator.Current.Clone();
                    dblOutstandingAmount = Convert.ToDouble(nav2.Select("Customer").Current.SelectSingleNode("OutstadingAmount").InnerXml);
                    strCustomerType = nav2.Select("Customer").Current.SelectSingleNode("CustomerType").InnerXml;
                }

                // Getting the allowed Outstanding Amount
                string strCustomerTypeFileName = context.Request.PhysicalApplicationPath + @"Data\CustomerTypes.xml";
                if (File.Exists(strCustomerTypeFileName))
                {
                    doc = new XPathDocument(strCustomerTypeFileName);
                    nav = doc.CreateNavigator();

                    iterator = nav.Select("//CustomerType[Id = '" + strCustomerType + "']");

                    while (iterator.MoveNext())
                    {
                        XPathNavigator nav2 = iterator.Current.Clone();
                        dblAllowedOutstandingAmount = Convert.ToDouble(nav2.Select("CustomerType").Current.SelectSingleNode("AllowedOutstadingAmount").InnerXml);
                    }

                    // Now the business logic to decide is it allowed.
                    bool IsProceed = false;
                    if ((dblOutstandingAmount + Convert.ToDouble(context.Request.Form["InvoiceAmount"].ToString())) <= dblAllowedOutstandingAmount)
                            IsProceed = true;

                    //We can even write as JSON object. But as we dont have any complex data structure we are using comma (,) only for separating the data values.
                    string[] values = new string[] { "successful", strCustomerID.ToString(), strCustomerType.ToString(), IsProceed.ToString(), dblOutstandingAmount.ToString(), dblAllowedOutstandingAmount.ToString() };

                    // JSON Serialize the array
                    System.Web.Script.Serialization.JavaScriptSerializer objSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
                    retValue = objSerializer.Serialize(values);
                }
                else
                    retValue = "unsuccessful";
            }
            else
                retValue = "unsuccessful";
        }
        else
            retValue = "unsuccessful";
    }
    catch
    {
        retValue = "unsuccessful";
    }
    context.Response.Write(retValue);
}
This code returns a JSON string which has "Successful" message, Customer Id, Customer Type, Whether to Proceed, Current Outstanding Amount, Allowed Outstanding Amount.

Note: I use XML files to simplify the implementation instead of using a database. The main reason of this post is to understand the client and server conversations and to get the user inputs between two server events.

Finally the JQuery for handing all the UI functionality defined below
$(document).ready(function () {
    // This function is for cancel button, when click of this button the popup screen will be hidden and show as normal.
    $(".Cancel").click(function () {
        $("#confirmShow").css("display", "none");
        $("#divHidden").css("display", "none");
    });
    // This function is called whe Proceed button clicked, and it calls the server event.
    $(".Proceed").click(function () {
        $(".btnServerSave").click();
    });
    // This function is called when clicking Save button.
    $(".btnSave").click(function (event) {
        if (($(".CustomerID").val().length == 0) || ($(".InvoiceAmount").val().length == 0)) {
            alert("Enter Customer Id and Invoice Amount for proceeding further");
            event.preventDefault();
        }
        else {
            $("#divHidden").css("display", "block");
            $("#progrssShow").css("display", "block");
            jQuery.post('CustomerHandler.ashx',
                        { CustomerID: $(".CustomerID").val(), InvoiceAmount: $(".InvoiceAmount").val() },
                        function (data) {
                            var ArrValue = eval(data); // Convert the JSON object as array
                            if (ArrValue[0] != "unsuccessful") {
                                if (data == "NotExist") {
                                    alert('There is something gone wrong. Please contact Administrator.');
                                    $("#divHidden").css("display", "block");
                                    $("#progrssShow").css("display", "none");
                                    return false;
                                }
                                else if (ArrValue[0] == 'successful') {
                                    if (ArrValue[3] == "False") {
                                        $("#progrssShow").css("display", "none");
                                        $("#confirmShow").css("display", "block");
                                      var msg = "<b style='color:red; padding-left:40%'>Alert!!!</b>" +
                                                "The invoice amount exceeded the allowed outstanding amount!!!. " +
                                                "Outstanding Amount : " + ArrValue[4]  +
                                                "Allowed Outstanding Amount : " + ArrValue[5] +
                                                "Do you want to proceed...";
                                        $("#Message").html(msg);
                                    }
                                    else {
                                        $(".btnServerSave").click();
                                    }
                                } else {
                                    return true; // Will not happen as per handler
                                }
                            }
                            else {
                                alert('There is something gone wrong. Please contact Administrator.');
                                return false;
                            }
                        }
                    );
        }
    });
});

Below is the video


Download the working source code in C# here and in VB here.


6 Responses to “User Interaction between server executions in ASP.NET”

Post a Comment