TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

InProc to SqlServer Session problem

I'm running a long process from one of my aspx pages, so I call BeginInvoke on an object method passing in an object called "state". "state" has three properties (among other info) that include PercentComplete, StatusMessage, and Results. This works out well because I put the "state" object into Session and then I can pass the "state" object as a param to BeginInvoke. Otherwise, the thread on BeginInvoke has no context for the Session.

After BeginInvoke is called, enable a JavaScript code snippet on the page to run which uses MSXML to make an HTTP request that returns XML containing the PercentComplete and Status values. That HTTP request is to an aspx page that looks at the "state" object in Session, formats the values into XML and returns a response. The javascript updates the progress on the page called by the JavaScript. So, in the long running process, I can set PercentComplete and StatusMessage values using the "state" object I passed in using BeginInvoke, the other page can read the values in the "state" object by accessing it via Session.

OK, this all works fine and dandy right now because Session is stored in- process. Now, I have to make the transition to storing state in SQL Server. That's a problem because the "state" object is no longer a reference in memory, it is serialized and stored in SQL Server. Does anyone have ideas on how to re-work this particular problem?

Here's an example of the code I'm taking about in case my description didn't make sense. The names "NorthWind" and "State" are just examples.
This solution is incomplete as far as processing results, but it gets you hooked into the scenario I'm talking about as far as the reference to the same object in Session and the param passed via BeginInvoke.

Like I said, this works great with Session running InProc, but as soon as I make the transition to SqlServer for Session state storage...I'm dead in the water. I have a few ideas for an alternate solution, but I am soliciting advice so that I can come up with the best possible solution. Any takers?


------------------- State.cs ------------------------------------

 

public class State

{

    public int PercentComplete;

    public string StatusMessage;

    public object Results;

}


----------------- Northwind.cs ------------------------------
 

public class Northwind

{

    public void LongRunningOperation(State state)

    {

        for(int i = 0; i < 10; i++)

        {

            System.Threading.Thread.Sleep(1000);

            state.PercentComplete = i * 10;

            state.StatusMessage = "Doing something @ " + DateTime.Now.ToLongTimeString();

        }

    }

}

---------------------- ProgressInfo.aspx ----------------------------

 

<%@ Page language="c#" Codebehind="ProgressInfo.aspx.cs" AutoEventWireup="false" Inherits="WebApplication6.ProgressInfo" %>

<% = XmlResponse %>


---------------------- ProgressInfo.cs -------------------------------

 

public class ProgressInfo : System.Web.UI.Page

{

    private string _xmlResponse;

    private State _state = null;

 

    public string XmlResponse

    {

        get

        {

            string s = this._xmlResponse;

            this._xmlResponse = String.Empty;

            System.Diagnostics.Debug.Write(s);

            System.Diagnostics.Debug.Flush();

            return s;

        }

    }

 

      private void Page_Load(object sender, System.EventArgs e)

      {

        Response.ContentType = "text/xml";

        XmlTextWriter writer = null;

       

        StringBuilder sb = new StringBuilder();

        TextWriter swriter = new StringWriter(sb);

 

        this._state = Session["state"] as State;

        int percentComplete = this._state.PercentComplete;

       

        writer = new XmlTextWriter(swriter);

        // Use indenting for readability.

        writer.Formatting = Formatting.Indented;

        writer.Indentation= 4;

       

        // Write an element (this one is the root).

        writer.WriteStartElement("Status");

       

        writer.WriteStartElement("StatusMessage");

        writer.WriteString(this._state.StatusMessage);

        writer.WriteEndElement();

 

        writer.WriteStartElement("PercentComplete");

        writer.WriteString(percentComplete.ToString());

        writer.WriteEndElement();

 

        // Write the close tag for the root element

        writer.WriteEndElement();

 

        // Write the XML to file and close the writer

        writer.Flush();

        writer.Close();

            

        this._xmlResponse = sb.ToString();

      }

}

----------------- WebForm1.cs ---------------------------------

 

public class WebForm1 : System.Web.UI.Page

{

    protected System.Web.UI.WebControls.LinkButton linkButtonStartLongRunningProcess;

    protected System.Web.UI.WebControls.Label labelStatusMessage;

    protected System.Web.UI.WebControls.Label labelPercentComplete;

 

    protected string statusPageUrl;

    delegate void LongProcDelegate(State state);

 

    private void linkButtonStartLongRunningProcess_Click(object sender, System.EventArgs e)

    {

        Northwind nwind = new Northwind();

        LongProcDelegate del = new LongProcDelegate(nwind.LongRunningOperation);

        //Create "state" object and then put into Session for ProgressInfo.aspx to "read"

        State state = new State();

        Session["State"] = state;

        //Pass "state" object into long running operation to update status

        AsyncCallback callBack = new AsyncCallback(this.SomeCallBackFunc);

        del.BeginInvoke(state, callBack, null);

        this.statusPageUrl = "http://localhost/WebApplication6/ProgressInfo.aspx";

    }

 

    private void SomeCallBackFunc(IAsyncResult result)

    {

        //read results...etc

    }

   

      override protected void OnInit(EventArgs e)

      {

            InitializeComponent();

            base.OnInit(e);

      }

   

      private void InitializeComponent()

      {   

        this.linkButtonStartLongRunningProcess.Click += new System.EventHandler(this.linkButtonStartLongRunningProcess_Click);

        this.Load += new System.EventHandler(this.Page_Load);

 

    }

}



------------------------- WebForm1.aspx -----------------------------------

 

<form id="Form1" method="post" runat="server">

    <asp:LinkButton ID="linkButtonStartLongRunningProcess" Runat="server">Start</asp:LinkButton>

    <br>

    <br>

    <asp:Label ID="labelStatusMessage" Runat="server"></asp:Label><br>

    Percent Complete:

    <asp:Label ID="labelPercentComplete" Runat="server"></asp:Label>

    <script language="javascript">

                var statusUrl = new String("<%=statusPageUrl %>");

                var iIntervalId = 0;

               

                if(statusUrl.length > 0 && iIntervalId == 0) {

                    BeginProgressUpdate();

                }

               

                    function BeginProgressUpdate() {

                          iIntervalId = window.setInterval("UpdateProgressMeter()", 100);

                    }

            

                    function EndProgressUpdate() {

                          window.clearInterval(iIntervalId);

                    }

               

                    function UpdateProgressMeter() {

                        var oXmlHttp = new ActiveXObject("Microsoft.XMLHTTP");

                  var oXmlDoc = new ActiveXObject("MSXML2.DOMDocument");

                      

                  var currentDate = new Date();

                      var rndQueryString = currentDate.getFullYear().toString() + currentDate.getMonth().toString() + currentDate.getDay().toString() + currentDate.getHours().toString() + currentDate.getMinutes().toString() + currentDate.getSeconds().toString() + currentDate.getMilliseconds().toString();

   

                      oXmlHttp.open("GET", statusUrl + "?time=" + rndQueryString, false);

                      oXmlHttp.send();

                    

                      oXmlDoc.loadXML(oXmlHttp.responseXML.xml);

                      

                        var statusMessage = "";

                        var percentComplete = 0;

                      

                      statusMessage = oXmlDoc.selectSingleNode("/Status/StatusMessage").text;

                      percentComplete = parseInt(oXmlDoc.selectSingleNode("/Status/PercentComplete").text);

                    

                    document.all("labelPercentComplete").innerText = percentComplete.toString() + "%";

                    document.all("labelStatusMessage").innerText = statusMessage + "%";

                    }

    </script>

</form>


Digg!

posted on Saturday, July 24, 2004 8:46 AM

Feedback

No comments posted yet.