Archives

Archives / 2009 / June
  • AH, Ah, ah, ah…Staying Alive…Staying Alive

    Sometimes you want your web page to 'stay alive'. That is, if a user is filling out a complicated form, you do not want the session to time out before they are finished. The user could get very angry and rightfully so: You might even get yelled at!

    It's not simply a matter of increasing the session timeout to a very large value. If you do that, the sessions would be left active in the server memory for hours—long after the visitors have left the site. Increasing the session timeout IS a solution… but not necessarily a good solution.

    The goal is that the session should stay active as long as the web page is open on the client machine …even if there are no post backs to reset the session timer. When the web page is closed, the session should time out normally.

    I implemented a solution for this: The client will "ping" the server at intervals of less than the session timeout which will reset the session timer. This is known as the Heartbeat design pattern (I couldn't find a decent site/page to link to).

    Miscellaneous Setup Stuff:

    For testing purposes, I set the Session Timeout to two minutes in web.config:

    <system.web>
      <sessionState timeout="2">
    </sessionState>

     

    To trace what is happening, I used a utility function called ODS (it's in a class called MiscUtilities): 
    /// ---- ODS ---------------------------------------
    /// <summary>
    /// Output Debug String with time stamp.
    /// </summary>
     
    public static void ODS(string Msg)
    {
        String Out = String.Format("{0}  {1}", 
                           DateTime.Now.ToString("hh:mm:ss.ff"), Msg);
        System.Diagnostics.Debug.WriteLine(Out);
    }

     

    To watch the Session State events, I added debugging strings to the global.asax file:

    <%@ Application Language="C#" %>
     
    <script RunAt="server">
     
        void Application_Start(object sender, EventArgs e)
        {
            MiscUtilities.ODS("****ApplicationStart");
        }
        void Session_Start(object sender, EventArgs e)
        {
            MiscUtilities.ODS("Session_Start");
        }
        void Session_End(object sender, EventArgs e)
        {
            MiscUtilities.ODS("Session_End");
        }

    Here are the details:

    We need a method at the server for the client to call. We use a WebMethod.

    1. There must be a ScriptManager on the page.
    2. The ScriptManager must have EnablePageMethods set to true.
    3. The WebMethod must be public and static.
    4. The WebMethod must have the EnableSession attribute set to true.
    <asp:ScriptManager ID="ScriptManager1" runat="server" 
        EnablePageMethods="true">
    </asp:ScriptManager>
     
    public partial class _Default : System.Web.UI.Page
    {
        [WebMethod(EnableSession=true ) ]
        public static void PokePage()
        {
            // called by client to refresh session
            MiscUtilities.ODS("Server: I am poked");       
        }

     

    We need JavaScript at the client to call the server function at fixed intervals:

    <script type="text/javascript">
     
        var HeartBeatTimer;
     
        function StartHeartBeat()
        {
            // pulse every 10 seconds
            if (HeartBeatTimer == null)
                HeartBeatTimer = setInterval("HeartBeat()", 1000 * 10);
        }
     
        function HeartBeat()
        {
            // note: ScriptManger must have: EnablePageMethods="true"
            Sys.Debug.trace("Client: Poke Server");
            PageMethods.PokePage();
        }
     
    <body id="MyBody"  onload="StartHeartBeat();">

     

    Here is what the output looks like without the heartbeat:

    10:22:43.03  ****ApplicationStart
    10:22:45.13  Session_Start
    10:25:00.00  Session_End 

    Here is the output with the heartbeat:

    10:26:06.10  ****ApplicationStart
    10:26:08.05  Session_Start
    Client: Poke Server
    10:26:18.93  Server: I am poked
    Client: Poke Server
    10:26:28.95  Server: I am poked
    Client: Poke Server
    10:26:38.96  Server: I am poked
    Client: Poke Server
    10:26:48.98  Server: I am poked

        . . . (lines deleted)

    Client: Poke Server
    10:29:59.45  Server: I am poked
    Client: Poke Server
    10:30:09.47  Server: I am poked
    Client: Poke Server
    10:30:19.48  Server: I am poked

        . . . (lines deleted)

    It looks like the session is staying alive while the client is idle: Excellent!

    I hope someone finds this useful.

    Steve Wellens

  • Why goto Still Exists in C#

    Developers, Software Engineers, and Programmers are logical, rational, reasonable people right? Sure they are…until you disagree with something they believe in. Then they can become the most enflamed, outraged, foaming-at-the-mouth, intolerant, lunatics you've ever had the pleasure of meeting.

    Take for instance the goto command. It can create emotions as intense as those raised during the ancient 'tabs versus spaces' debates or whether or not curly braces should be aligned in columns. (For the record, developers who use tabs and don't line up curly braces also kick puppies and do not practice good hygiene).

    You can program for years and never use a goto. However, there are times when a goto can make the code simpler and that…is a very good thing.

    Here's a scenario: You are working on a multi-threaded real-time program. You can't use the debugger because stepping through the code would mess up the timings and interactions between the threads. You also need something to help diagnose problems in the field.  A runtime log that can be turned on and off is used. The requirement is that every function will log its entry and exit point. That way if something goes wrong, the log will show what function failed and where it failed.

    In one area of the program, you need to perform several steps in sequence. If any step fails, the remaining steps must not execute.

    Here's the first attempt:  

    // Do Process using nested if statements
    void DoProcess1()
    {
        LOG("DoProcess Started...");
     
        if (Step1() == true)
            if (Step2() == true)
                if (Step3() == true)
                    if (Step4() == true)
                        if (Step5() == true)
                            if (Step6() == true)
                                Step7();
     
        LOG("DoProcess Finished");
    }

    Sure it works but getting code to work is only the first step. Creating clear, maintainable code is the goal. If the code can be simplified, you are not done.

    Second attempt, use a flag variable:

    // Do Process using a success flag
    void DoProcess2()
    {
        LOG("DoProcess Started...");
     
        bool Success;
     
        Success = Step1();
        if (Success == true)
            Success = Step2();
        if (Success == true)
            Success = Step3();
        if (Success == true)
            Success = Step4();
        if (Success == true)
            Success = Step5();
        if (Success == true)
            Success = Step6();
        if (Success == true)
            Success = Step7();
     
        LOG("DoProcess Finished");
    }

    That's better but it can be simplified further:

    Third attempt with goto:

    // DoProcess using goto
    void DoProcess3()
    {
        LOG("DoProcess Started...");
     
        if (Step1() == false)
            goto EXIT;
        if (Step2() == false)
            goto EXIT;
        if (Step3() == false)
            goto EXIT;
        if (Step4() == false)
            goto EXIT;
        if (Step5() == false)
            goto EXIT;
        if (Step6() == false)
            goto EXIT;
        if (Step7() == false)
            goto EXIT;
     
    EXIT:
        LOG("DoProcess Finished");
    }

    The creation, assigning and checking of a variable has been eliminated. It also runs faster but the speed improvement is insignificant and not a reason for using a goto.

    The example is trivial, however in real life, being able to jump to the end of complicated functions can dramatically reduce the complexity of code.

    Before you disagree with the inclusion of the goto in the C# language, remember you are disagreeing with the people who created the language. You are also disagreeing with Steve McConnel the author of "Code Complete". Here's his chapter on gotos.

    In my career, I've only used a goto once and I had to present the code to a code-review group of four developers. When I showed the code with and without the goto, they unanimously agreed without any discussion that goto was…the way to go. Pun intended.

    I hope someone finds this helpful.

    [Update: July 3, 2009]

    Here are two more examples of when a goto is helpful. The first is from Charles Petzold’s book “.Net Book Zero” a free pdf book available at http://www.charlespetzold.com/

    A switch “fall through” is illegal in C# (this causes a compiler error):

    switch (a) 
    { 
        case 3: 
            b = 7; 
        case 4: 
            c = 3; 
            break; 
        default: 
            b = 2; 
            c = 4; 
            break; 
    }

    To get it to work you can use a goto:

    switch (a) 
    { 
        case 3: 
            b = 7;
            goto case 4;
        case 4: 
            c = 3; 
            break; 
        default: 
            b = 2; 
            c = 4; 
            break; 
    }
     

    This example shows better how to cleanly get out of nested loops/code.  The task is to search a three dimensional array and check for a null value:

    bool GetData()
    {
        String[,,] Data = new String[5, 5, 5];
     
        // ....code to fill in array here
     
        for (int x = 0; x < 5; x++)
        {
            for (int y = 0; y < 5; y++)
            {
                for (int z = 0; z < 5; z++)
                {
                    if (Data[x, y, z] == null)
                        goto NULL_FOUND;
                }
            }
        }
     
        return true;
     
    NULL_FOUND:
        Response.Write("Invalid Data");
        return false;
    }

    Steve Wellens