2
\$\begingroup\$

Now this is something I've looked into, and while I have a "working" solution, I don't like it.

Background:

Through our intranet website, we want to run a process that copies a file from another machine, unzips it, and then analyzes the content. As the file is large, this takes some time (usually around 5-6 minutes). Rather than have the user just hit a Button and pray that they get a success message in 5-6 minutes, we want to show the progress via updates to a TextBox.

What I've learned so far:

This isn't as simple as putting everything into an UpdatePanel and updating it at various steps in the process. Seems like it would be, but it's not. I looked into threading as well, but I couldn't get it working. That is to say, I got the process to run on a separate thread, but while it was running, the interface wouldn't update. It would queue up everything, and then display it all at once, once the process finished. As I'm still relatively new, the possibility that I was just doing something wrong is high.

What I have (which works, I guess...):

Two .aspx pages, DatabaseChecker.aspx and Processing.aspx

DatabaseChecker.aspx:

<form id="form1" runat="server">
        <asp:Button ID="btnExecute" runat="server" onclick="btnExecute_Click" 
            style="height: 26px" Text="Execute" />

    <iframe src="Processing.aspx" name="sample" width="100%" height="700px" style="border-style: none; overflow: hidden;">
    </iframe>
</form>

Processing.aspx:

<meta http-equiv="refresh" content="1" />

<form id="form1" runat="server">
    <asp:TextBox ID="TextBox1" runat="server" Height="633px" TextMode="MultiLine" 
        Width="504px"></asp:TextBox>
</form>

The .aspx portion is very simple. DatabaseChecker.aspx simply has a button to begin the process, and it has Processing embedded as an iframe.

The result looks like this:

DatabaseChecker.aspx

The TextBox1 in Processing.aspx is where the progress update goes.

Now let me just point out the dirty trick and the part I don't like right now. In Processing.aspx, there is a meta tag to refresh the page once per second.

How it works (summary):

When the process starts, a Session variable called ["Running"] is set to true. When the process ends, Session["Running"] is set to false. And since Processing.aspx refreshes once per second, what happens is it saves the current contents of TextBox1.Text to another Session variable called ["TextBoxContent"]. And then the Page_Load method for Processing.aspx fills the TextBox back up with the previous content, and adds a period.

So the output will begin simply looking like "Process Starting", but after 10 seconds it will look like "Process Starting.........." (one period per second).

How it works (details):

The process begins in DatabaseChecker.aspx's Execute button:

protected void btnExecute_Click(object sender, EventArgs e)
{
    Session["TextBoxContents"] = "Copying .zip file from other machine...";
    Session["Running"] = true;
    Thread thread = new Thread(new ThreadStart(TheProcess));
    thread.IsBackground = true;
    thread.Start();
}

private void TheProcess()
{
    CopyFromOtherMachine();
    UnzipFiles();
    ConvertTextFilesToDataTables();
    //and so on and so forth
    Session["Running"] = false;
}

private void CopyFromOtherMachine()
{
        if (File.Exists(Path.Combine(FileRootDirectory, DesiredFileName)))
        {
            Session["TextBoxContent"] += "Previous .zip file already detected.  Deleting...";
            File.Delete(Path.Combine(FileRootDirectory, DesiredFileName));
            Session["TextBoxContent"] += "OK!" + Environment.NewLine;
        }
        Session["TextBoxContent"] += "Copying .zip from other machine...";
        File.Copy(@"\\someothermachine\production\desiredfile.zip", Path.Combine(FileRootDirectory, DesiredFileName));
        Session["TextBoxContent"] += "OK!" + Environment.NewLine;
}

// UnzipFile()
// ConvertTextFilesToDataTables()
// and so on

And then we have Processing.aspx's Page_Load method, which is where we display our progress:

protected void Page_Load(object sender, EventArgs e)
{
    if (Session["TextBoxContent"] != null)
    {
        TextBox1.Text = Session["TextBoxContent"].ToString();
        if ((bool)Session["Running"] != false)
        {
            Session["TextBoxContent"] += ".";
        }
    }
}

What I want to improve:

Basically, everything. This whole thing feels like a really makeshift house of cards. In particular, I don't like that the page has to update once per second, particularly because the Windows mouse icon changes to loading and not loading cursors very fast. If the user knows the system and knows what's going on, then yeah big deal I guess, we can just deal with it because we know what's going on. But I think to the average user, the behavior is jarring, it feels like something might be wrong or something.

Hopefully I've made it clear what I'm trying to achieve overall, so I'm open to other ideas about how to go about it.

\$\endgroup\$
  • \$\begingroup\$ did you tried using SignalR ? \$\endgroup\$ – Kiquenet Dec 31 '18 at 13:09
  • \$\begingroup\$ async/await method better than ThreadStart ? or using hanselman.com/blog/… \$\endgroup\$ – Kiquenet Feb 8 at 9:35
2
\$\begingroup\$

Have you considered using SignalR?

As their homepage states, it's a "library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time."

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.