在.NET Web Control中异步显示存储过程PRINT语句

时间:2014-02-26 18:58:35

标签: c# asp.net sql stored-procedures asynchronous

我已经编写了一个很长的存储过程,我从ASP.NET(3.5)页面调用。

存储过程如下所示:

CREATE PROCEDURE csi_CourseEval_Export
   @year
   @term
   @use_case
AS
BEGIN
   SET NOCOUNT ON
   PRINT 'Building candidate course list'
   .... -- do stuff
   PRINT ''
   RAISERROR('1', '0', 1) WITH NOWAIT -- flush buffer and indicate step 1 reached
   PRINT ''
   .... -- do more stuff
   PRINT ''
   RAISERROR('2', '0', 1) WITH NOWAIT -- flush buffer and indicate step 2 reached
   PRINT ''
   .... -- do more stuff
   PRINT ''
   RAISERROR('3', '0', 1) WITH NOWAIT -- flush buffer and indicate step 3 reached
   PRINT ''
   .... -- do more stuff
   .... -- do LOTS more stuff, including steps 4 through 8
   PRINT ''
   RAISERROR('9', '0', 1) WITH NOWAIT -- flush buffer and indicate step 9 reached
   PRINT ''       

   RAISERROR('DONE', '0', 1) WITH NOWAIT -- flush buffer and indicate completion

END

我的C#codebehind具有以下相关的属性和方法:

    private SqlConnection db;
    private string exportState = "";
    private static string jdbConnectionString = "Data Source=...;Initial Catalog=...;Persist Security Info=True;Asynchronous Processing=true;User ID=...;Password=...";

    private void ShowData(GridView display, string table)
    {
        display.DataSource = new SqlDataSource(CoursEval.jdbConnectionString, "SELECT * FROM ##" + table);
        display.DataBind();
    }

    private void SetControls(bool enable)
    {
        RunExport.Enabled = enable;

        if (enable)
            RunExport.Text = "Run";
        else
            RunExport.Text = "Running";

        UseCase.Enabled = enable;
        Year.Enabled = enable;
        Term.Enabled = enable;
    }

    private void LogSQL(string message)
    {
        if (message.Length == 1 || message.Equals("DONE"))
        {
            exportState = message;
        }
        SQL_Log.Text += "<br />" + message.Replace(" ", "&nbsp;").Replace("\n", "<br />");
    }

    protected void RunExport_Click(object sender, EventArgs e)
    {
        db = new SqlConnection(jdbConnectionString);

        db.FireInfoMessageEventOnUserErrors = true;            
        db.InfoMessage += delegate(object ds, SqlInfoMessageEventArgs de)
        {
            LogSQL(de.Message);              
        };
        db.Open();

        SqlCommand cmd = new SqlCommand("csi_CourseEval_Export", db);
        cmd.Parameters.Add("@year", SqlDbType.VarChar).Value = Year.SelectedValue;
        cmd.Parameters.Add("@term", SqlDbType.VarChar).Value = Term.SelectedValue;
        cmd.Parameters.Add("@use_case", SqlDbType.VarChar).Value = UseCase.SelectedValue;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandTimeout = 0;

        SetControls(false);
        cmd.ExecuteNonQuery();

        ShowData(ShowCourses, "Courses");
        ShowData(ShowStudents, "Students");
        ShowData(ShowFaculty, "Faculty");

        ResultsPanel.Visible = true;

        db.Close();
        SetControls(true);
    }

以下System.Web.UI.WebControls.*表单对象是相关的:

SQL_Log                                - Label
RunExport                              - Button
UseCase, Year and Term                 - DropDownList 
ShowCourses, ShowFaculty, ShowStudent  - GridView
ResultsPanel                           - Panel

症状:
无论我做什么,SQL_Log标签只会在存储过程的最后更新一次,而其他所有内容都会显示。

期望的行为:
SQL_Log标签应该异步更新,让用户看到存储过程已经走了多远。

我做错了什么?

编辑: 这是一个部分答案:

更新以使用异步SqlCommand(BeginExecuteNonQuery而不是ExecuteNonQuery)让我进入第一个RAISERROR,但是在调用EndExecuteNonQuery之后我不会再收到任何InfoMessage事件,即使存储过程仍在此时运行

RunExport_Click现在看起来像:

    protected void RunExport_Click(object sender, EventArgs e)
    {
        db = new SqlConnection(CoursEval.jdbConnectionString);

        db.FireInfoMessageEventOnUserErrors = true;            
        db.InfoMessage += delegate(object ds, SqlInfoMessageEventArgs de)
        {
            LogSQL(de.Message);              
        };
        db.Open();

        SqlCommand cmd = new SqlCommand("csi_CourseEval_Export", db);
        cmd.Parameters.Add("@year", SqlDbType.VarChar).Value = Year.SelectedValue;
        cmd.Parameters.Add("@term", SqlDbType.VarChar).Value = Term.SelectedValue;
        cmd.Parameters.Add("@use_case", SqlDbType.VarChar).Value = UseCase.SelectedValue;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.StatementCompleted += GetExportResults;
        cmd.CommandTimeout = 0;

        SetControls(false);

        cmd.BeginExecuteNonQuery(new AsyncCallback(FinishExport), cmd);

    }

我现在有三个额外的功能,ShowResults,GetExportResults和FinishExport:

    private void ShowResults()
    {
        ShowData(ShowCourses, "Courses");
        ShowData(ShowFaculty, "Faculty");
        ShowData(ShowFacultyEnrollment, "FacultyEnrollment");
        ShowData(ShowStudents, "Students");
        ShowData(ShowStudentEnrollment, "StudentEnrollment");

        ResultsPanel.Visible = true;

    }


    private void FinishExport(IAsyncResult result)
    {
        if (result.IsCompleted)
        {
            SqlCommand cmd = (SqlCommand)result.AsyncState;

            cmd.EndExecuteNonQuery(result);
        }
        else
        {
            RunExport.Text = exportState;
        }
    }

    private void GetExportResults(object sender, StatementCompletedEventArgs e)
    {
        ShowResults();

        db.Close();
        SetControls(true);
    }

此外,LogSQL现在看起来像这样:

    private void LogSQL(string message)
    {
        if (message.Length == 1 || message.Equals("DONE"))
        {
            exportState = message;
        }

        SQL_Log.Text += "<br />" + message.Replace(" ", "&nbsp;").Replace("\n", "<br />");
        ProgressPanel.Update();
    }

其中ProgressPanel是AJAX驱动的UpdatePanel,其中包含所有内容。

脚本现在到达第2阶段RAISERROR,此时FinishExport将触发,我不会再收到任何InfoMessage事件。 GetExportResults永远不会触发,因此整个页面基本上都会挂起。我错过了什么?

1 个答案:

答案 0 :(得分:0)

无法异步更新标签 - Web应用程序无法正常工作。当您的页面执行时,它会将HTML发送到浏览器,然后销毁。没有简单的过程可以继续将更新的HTML发送到浏览器。