Ожидание выполнения длительного процесса в ASP.NET

Допустим, у нас есть страница Default.aspx, на которой пользователь вводит имя клиента и номер накладной, после чего происходит проверка этого номера. Поскольку проверка может занимать длительное время, то необходимо обеспечить пользователя информацией о ходе выполнения процесса (идет или уже прошел). Наипростейший способ приведен ниже.

Default

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;

public partial class _Default : System.Web.UI.Page
{

    protected Guid _id;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            _id = Guid.NewGuid();

            ThreadStart ts = new ThreadStart(Check);
            Thread th = new Thread(ts);
            th.Start();

            Response.Redirect("Invoice.aspx?ID=" + _id.ToString());
        }
    }

    protected void Check()
    {
        string res = InvoiceProcessor.CheckInvoice(txtName.Text, txtNumber.Text);
        CheckedInvoices.Add(_id, res);
    }

}

Суть в том, что создается уникальный идентификатор. По которому результат будет получен из хэш-таблицы результатов. Пример класса таблицы CheckedInvoices и класса-заглушки, который в нормальном приложении выполняет проверку InvoiceProcessor, приведены ниже.

CheckedInvoices

using System;
using System.Collections;

public static class CheckedInvoices
{
    private static Hashtable _invoices = new Hashtable();

    public static string GetResult(Guid id)
    {
        if (_invoices.Contains(id))
        {
            return Convert.ToString(_invoices[id]);
        }
        else
        {
            return "";
        }
    }

    public static void Add(Guid id, string value)
    {
        _invoices[id] = value;
    }

    public static void Remove(Guid id)
    {
        _invoices.Remove(id);
    }
}

InvoiceProcessor

using System;
using System.Threading;

public static class InvoiceProcessor
{
    public static string CheckInvoice(string customerName, string invoiceID)
    {
        Thread.Sleep(10000);
        if (invoiceID == "qwerty")
        {
            return "Текущий статус: передано в службу доставкиnОжидаемое время доставки: черт его знает";
        }
        else if(invoiceID == "asdfgh")
        {
            return "Текущий статус: доставленоnОжидаемое время доставки: дык уже";
        }
        else
        {
            return "Нет информации по данной накладной";
        }
    }
}

Страница, на которую выполняется переадресация, должна лишь проверять готовность результата.

Invoice

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class Invoice : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.Params["ID"] != null)
        {
            Guid id = new Guid(Request.Params["ID"]);

            if (CheckedInvoices.GetResult(id).Length == 0)
            {
                phInvoice.Controls.Add(new LiteralControl("Пожалуйста, подождите..."));
                Response.AddHeader("Refresh", "5");
            }
            else
            {
                phInvoice.Controls.Add(new LiteralControl(CheckedInvoices.GetResult(id)));
                CheckedInvoices.Remove(id);
            }
        }
        else
        {
            phInvoice.Controls.Add(new LiteralControl("Не указан идентификатор накладной."));
        }
    }
}

Схема очень проста — на странице Default.aspx происходит постбэк, в этот момент создается новый уникальный идентификатор, который передается методу, выполняемумо в отдельном процессе. После этого происходит переадресация на страницу Invoice.aspx, где происходит проверка по идентификутору (переданому в GET запросе) в таблице CheckedInvoices, нет ли там данных. Если данных нет, то посылается заголовок, сообщающий браузеру о необходимости сделать Refresh, если же результат появился, то пользователю выводится результат, а ключ удаляеся из хэш-таблицы, поскольку больше не нужен.