【问题标题】:how to get Long running process completion status using WebService and Jquery AJAX?如何使用 WebService 和 Jquery AJAX 获取长时间运行的进程完成状态?
【发布时间】:2011-08-25 04:35:52
【问题描述】:

我已经创建了将批量数据上传到服务器(sql 数据库)的 Web 服务。

我创建了两种方法,一种是为 在服务器和其他上上传数据是为了从保存的记录中获取状态,它将继续监视 UploadingData 方法并每秒向客户端显示状态。

这里是sn-p的代码:

AJAX

//------------------------------
        // Save uploaded file data in database
        //------------------------------
        function SaveFileData() {
            DisplayMessage("Uploading bulk data from file to database, this will take time please wait...", "Loading", false);

            //This will try to diaplay status of saved records in database
            displayRecordStatusOn();

            $.ajax({
                type: "POST",
                url: "SaveData.asmx/SaveFileData",
                data: "{'FileName':'" + savedFileName + "', 'ClientID':'" + GetSelectedClient() + "','FileAutoID':'" + savedFileAutoID + "'}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (response) {
                    var result = jQuery.parseJSON(response.d);
                    if (result.Success == "False") {
                        DisplayMessage("Error : " + result.Message, "Failed", false);
                        return;
                    }
                    else {
                        //DisplayMessage("Datatransfer process 100% completed", "Success", false);
                        displayRecordStatusOff();
                        DisplayMessage(result.Message, "Success", false);
                        alert("100% Done!!");
                    }
                }
            });
        }

//This method called every second and get result from SaveFileData (web service method)
function displayRecordStatusOn() {
            $.ajax({
                type: "POST",
                url: "SaveData.asmx/GetRecordsInsertStatus",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (response) {
                    var result = jQuery.parseJSON(response.d);
                    if (result.Success == "False") {
                        console.log("F: " + result.Message);
                    }
                    else {
                        console.log("S: " + result.Message);
                    }
                }
            });
            recordStatusTimer = setTimeout("displayRecordStatusOn()", 1000);
        }

网络服务

 string RecordStatus = "";
     [WebMethod]
            [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
            public string SaveFileData(string FileName, string ClientID, string FileAutoID)
            {
//--HttpContext.Current.Session["CurrentCount"] = 0;
            HttpContext.Current.Cache.Remove("Counter" + FileAutoID);
                try
                {
                    DataTable dtExcelData = ExcelDataLoader(FileName, ClientID, "");
                    long TotalRecords = dtExcelData.Rows.Count;
                    long CopiedRecords = 0;
                    using (SqlConnection cn = new SqlConnection(GetConnectionString()))
                    {
                        SqlCommand cmdCopiedRecords = new SqlCommand("SELECT COUNT(*) FROM " + string.Format(DataBankTableFormat, FileAutoID) + ";", cn);
 //--HttpContext.Current.Session["TotalCount"] = TotalRecords;
                    HttpContext.Current.Cache.Insert("Counter" + FileAutoID, TotalRecords, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
                        cn.Open();
                        using (SqlBulkCopy copy = new SqlBulkCopy(cn))
                        {
                            copy.BatchSize = BatchSize;
copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
                        copy.NotifyAfter = BatchSize;
                            for (int colIndex = 0; colIndex < dtExcelData.Columns.Count; colIndex++)
                            {
                                copy.ColumnMappings.Add(colIndex, colIndex);
                            }
                            copy.DestinationTableName = string.Format(DataBankTableFormat, FileAutoID);
                            copy.WriteToServer(dtExcelData);
                            CopiedRecords = System.Convert.ToInt32(cmdCopiedRecords.ExecuteScalar());
                            RecordStatus = string.Format("{0} of {1} copied successfully!", CopiedRecords, TotalRecords);
                        }
                        cn.Close();
                    }
                    Response = GetResponse(true, RecordStatus);
                }
                catch (Exception ex)
                {
                    Response = GetResponse(false, ex.Message);
                }
                return Response;
            }
           [WebMethod(EnableSession = true)]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public string GetRecordsInsertStatus(string FileAutoID)
        {
            try
            {
                Response = GetResponse(true, RecordStatus(FileAutoID));
            }
            catch (Exception ex)
            {
                Response = GetResponse(false, ex.Message);
            }
            return Response;
        }
    public string RecordStatus(string FileAutoID)
            {
                string response = "";
                //--response = string.Format("{0} of {1} copied successfully! at {2}", HttpContext.Current.Session["CurrentCount"], HttpContext.Current.Session["TotalCount"], DateTime.Now.ToString());
                response = string.Format("{0} of {1} copied successfully! at {2}", HttpContext.Current.Cache["Counter" + FileAutoID] == null ? "0" : HttpContext.Current.Cache["Counter" + FileAutoID].ToString(), 786, DateTime.Now.ToString());
                //return HttpContext.Current.Cache["Counter"+ClientID] == null ? "0" : HttpContext.Current.Cache["Counter"+ClientID].ToString();
                return response;
            }
            private static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
            {
                HttpContext.Current.Session["CurrentCount"] = e.RowsCopied;
            }

问题:

代码工作正常,但在此方法 (SaveFileData) 之后的 SaveFileData 过程中我没有获取 RecordStatus 变量的值。

  • 我有全局变量 RecordStatus,我正在更新它的值 SaveFileData 方法,我正在尝试从中获取价值 在客户端浏览器上显示的 GetRecordsInsertStatus 方法。 (但是这个 总是返回空白)

【问题讨论】:

  • 使用 ASMX WebService 是绝对必要的吗?你能在这个项目中使用 .NET 4.0 吗?如果第一个问题的答案是 no 而第二个问题的答案是 yes 我可以向您展示一个使用 PUSH 技术的示例,而不是让客户端 POLL 使用连续的 AJAX 请求。

标签: asp.net web-services jquery


【解决方案1】:

我认为正确的做法是使用SqlBulkCopy.NotifyAfter 属性。根据MSDN

此属性是为用户界面组件设计的 说明大容量复制操作的进度。它表明 在生成通知事件之前要处理的行数。

因此,利用该属性,我将修改您的代码如下:

添加一个获取计数的方法(只是一个细节)

private int GetCount(SqlConnection cn) {
    SqlCommand cmdCopiedRecords = new SqlCommand("SELECT COUNT(*) FROM " + string.Format(DataBankTableFormat, FileAutoID) + ";", cn);
    return System.Convert.ToInt32(cmdCopiedRecords.ExecuteScalar());
}

添加属性以返回格式化的 RecordStatus:

public string RecordStatus
{
    get { return string.Format("{0} of {1} copied successfully!", Session["CurrentCount"], Session["TotalCount"]); }
}

添加事件处理程序:

private static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
{
    Session["CurrentCount"] = (int)Session["CurrentCount"] + e.RowsCopied;
}

最后是修改后的SaveFileData 方法:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string SaveFileData(string FileName, string ClientID, string FileAutoID)
{
    try
    {
        DataTable dtExcelData = ExcelDataLoader(FileName, ClientID, "");
        long TotalRecords = dtExcelData.Rows.Count;
        long CopiedRecords = 0;
        using (SqlConnection cn = new SqlConnection(GetConnectionString()))
        {
            // NO LONGER NEEDED HERE - SqlCommand cmdCopiedRecords = new SqlCommand("SELECT COUNT(*) FROM " + string.Format(DataBankTableFormat, FileAutoID) + ";", cn);

            cn.Open();

            // ADDED - get an initial count
            Session["CurrentCount"] = GetCount(cn);

            // ADDED - store total
            Session["TotalCount"] = TotalRecords;

            using (SqlBulkCopy copy = new SqlBulkCopy(cn))
            {
                copy.BatchSize = BatchSize;

                // ADDED - Set up the event handler to notify after 50 rows.
                copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
                copy.NotifyAfter = BatchSize;

                for (int colIndex = 0; colIndex < dtExcelData.Columns.Count; colIndex++)
                {
                    copy.ColumnMappings.Add(colIndex, colIndex);
                }
                copy.DestinationTableName = string.Format(DataBankTableFormat, FileAutoID);
                copy.WriteToServer(dtExcelData);
            }
            cn.Close();
        }
        Response = GetResponse(true, RecordStatus);
    }
    catch (Exception ex)
    {
        Response = GetResponse(false, ex.Message);
    }
    return Response;
}

注意事项:

  • 我使用的是Session 而不是HttpContext.Current.Cache。这是因为Cache 是每个AppDomain (MSDN)。换句话说,它对每个用户都是一样的。所以如果你想根据用户来区分计数,这会给你错误的结果。
  • 如果您必须使用HttpCache,请使用HttpRuntime.Cache,因为它比HttpContext.Current.Cache 快一点。详情请见here

【讨论】:

  • 看基本上我想更新状态(65536中的1024复制成功!,65536中的2000复制成功!,65536中的5000复制成功!直到65536中的65536复制成功!...... .... 但我得到的不是 65536 复制成功!65536 复制成功!65536 复制成功!每秒当我的主要线程在数据库中传输数据时,我得到 65536 复制 65536成功!
  • @imdadhusen:您更新的代码将SessionHttpCache 混合在一起。我也注意到了其他几个错误。好像你把我的和伊卡洛斯的代码混在一起了。备份您现有的文件,删除您拥有的所有代码并完全复制我的代码(不是文件中的所有内容,而是我们正在讨论的方法)。
  • 我同意,但获取状态的逻辑与 Icarus 代码相同。你能告诉我我应该怎么做来更新状态我准备应用你的代码,如果它会给我结果。感谢您的支持。
【解决方案2】:

我不认为您可以按照您的方式初始化 RecordStatus 变量,因为每次调用 Web 服务上的方法时都会重新实例化它(这就是为什么您总是得到“空白”)。

您需要通过 SaveFileData 方法将状态保存/更新到数据库中的某种 temp 状态表,并不断读取该行以在 GetRecordsInsertStatus 方法中返回此信息。

编辑

我刚刚意识到,您也可以将此信息放入应用程序缓存中,在 SaveFileData 方法中插入并更新它,并在 GetRecordsInsertStatus 方法中从缓存中读取它。当应用程序缓存就是这样时,不需要带有 Hashtables 的静态类。

编辑 2: 我决定实施一个小示例项目,展示我最初回答的意思。我认为这很容易遵循。

下面是我的主页,混合了脚本和标记:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" language="javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>

    <script language="javascript" type="text/javascript">
        var pool = true;
        function displayRecordStatusOn() {
            $.ajax({
                type: "POST",
                url: "SaveData.asmx/GetRecordsInsertStatus",
                contentType: "application/json; charset=utf-8",
                data: '{"ClientID":"' + $('#CustomerIDs').val() + '"}',
                dataType: "json",
                success: function (response) {
                    if(pool)
                        $('#status').html('Current status: '+response.d);
                }
            });
            if(pool)
                setTimeout(displayRecordStatusOn, 1000);
        }

        function SaveFileData() {
            //This will try to diaplay status of saved records in database
            pool = true;
            $.ajax({
                type: "POST",
                url: "SaveData.asmx/SaveFileData",
                data: '{ "FileName": "whatever.txt" , "ClientID": "'+$('#CustomerIDs').val()+'" ,"FileAutoID":"1"}',
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (response) {
                    if (response.d == 'Done saving file') {
                        pool = false;
                        $('#status').html(response.d);
                    }
                }
            });
            displayRecordStatusOn();
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
         <input type="button" onclick="SaveFileData();" value="Start Upload Process" />
         <select id="CustomerIDs" >
         <option value="1">Customer 1</option>
         <option value="2">Customer 2</option>
         <option value="3">Customer 3</option>
         <option value="4">Customer 4</option>
         </select>
         <div id="status"></div>
    </div>
    </form>
</body>
</html>

基本上,页面最初看起来像这样:

网络服务代码如下:

[WebMethod]
    public string SaveFileData(string FileName, string ClientID, string FileAutoID)
    {
        HttpContext.Current.Cache.Remove("Counter"+ClientID);
        int counter = 0;
        bool process = true;
        while (process)
        {
            //This while loop executes for 10 seconds. This is so that I can simulate the long process the
            //questioner is asking
            Thread.Sleep(1000);
            counter++;
            //Append ClientID to the Key so that it can be uniquely identified since it's stored in Cache. 
            //If done in Session, no need to append the ClientID
            HttpContext.Current.Cache.Insert("Counter"+ClientID, counter, null, System.Web.Caching.Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, null);
            if (counter == 10)
                process = false;
        }
        return "Done saving file";
    }

    [WebMethod]
    public string GetRecordsInsertStatus(string ClientID)
    {
        //Added ClientID as parameter since we need to uniquely identify the key in the Cache
        return HttpContext.Current.Cache["Counter"+ClientID] == null ? "0" : HttpContext.Current.Cache["Counter"+ClientID].ToString();
    }

最后,这就是所有东西的外观(显示 2 个并发用户同时上传文件):

【讨论】:

  • 你说得对。我不喜欢第一种方式,因为它会打开/关闭连接并增加服务器的开销。所以我喜欢用 HashTable 创建静态类的第二种方法,而不是将所有详细信息存储在 HashTable 中,为什么我不只存储已保存记录的计数?否则它可能会创建安全大厅。
  • @imdadhusen 我想你误解了我的建议。我认为最好的方法是“状态”表。我不会担心打开/关闭数据库连接,因为这些东西是池化的。如果你妥善处理它们,我认为这不是一个真正的问题。具有静态 Hashtable 的静态类实际上是非常危险的,因为如果您不小心/特别小心,您可能会遇到线程问题和内存泄漏。我以前没有做过这样的事情,所以我建议你等待其他人发表评论。
  • 我在 SaveFileData 中添加了以下行 HttpContext.Current.Cache["RecordStatus"] = string.Format("{0} of {1} copied successfully! at {2}", CopiedRecords, TotalRecords, DateTime.Now.ToString()); 并在 GetRecordsInsertStatus 中添加了 return GetResponse(true, Convert.ToString(HttpContext.Current.Cache["RecordStatus"])); 但仍然没有得到输出
  • @imdadhusen 请查看我的更新答案。终于有一点时间来实现我最初回答的意思。我希望你觉得很容易理解。如果您需要,我可以提供整个项目代码。请告诉我。
  • 嗨 Lcarus,感谢您一直以来的支持,但到目前为止我还没有得到输出。我尝试了您的 (Cache) 和 Mrchief (Session) 建议,但没有得到结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2017-07-08
相关资源
最近更新 更多