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>