我在SharePoint中运行一个小应用程序,用于生成我们公司中某人所需的数据。简而言之,它将数据从Azure数据库提取到数据表中,然后将该数据表转换为excel。这可能需要大约一两分钟,有时,因为我们在新西兰并且在美国使用远程服务器,他们会得到超时,其中一个工作表无法加载。
因此,它在构建excel时的作用是遍历供应商列表,以及获取每个数据的财务周列表,并在每个数据表的Excel中创建单独的工作表。理想情况下,我想在用户看到的网格视图中添加新行, 正在构建报告 ,说明该财务周和供应商是否成功添加或不添加,因为excel报告在后端创建。这将允许用户更多地了解进度,并允许他们知道是否存在问题而不是猜测。
这是很多代码所以我会尽力向您展示相关部分。
拉取并创建Excel的方法
public void excelThreadCall()
{
DataTable updateDataTable = new DataTable();
gridView.DataSource = updateDataTable;
//Payments only download chosen Financial Week
using (XLWorkbook workbook = new XLWorkbook())
{
//gradeWeek = selectedGradeWeek.SelectedValue;
foreach (ListItem supplier in selectedSuppliers.Items)
{
if (supplier.Selected)
{
foreach (ListItem fWeek in selectedfWeeks.Items)
{
if (fWeek.Selected)
{
string checkEmptyTableSQL = @"SELECT COUNT(*) FROM FleshvGraded WHERE Supplier_Code = '" + supplier.Value + "' AND PO_Revision = " + fWeek.Value;
int rowCount = Convert.ToInt32(getVariable(checkEmptyTableSQL));
if (rowCount > 0)
{
foreach (ListItem report in selectedReports.Items)
{
//SQL Strings
string sqlIntakeDate = @"SELECT Week_Ending_Date FROM Fiscal_Calendar WHERE Fiscal_Week = LEFT(" + fWeek + ", 2) AND Fiscal_Year = CONCAT(20, RIGHT(" + fWeek + ", 2))";
string sqlPO = @"SELECT DISTINCT PO_No FROM FvGSummaryAll WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value;
string sqlAllSerials = "SELECT * FROM FvGData WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value
//variables
DateTime weekEnding = Convert.ToDateTime(getVariable(sqlIntakeDate));
DateTime weekStarting = weekEnding.AddDays(-5);
string fWeekString = fWeek.ToString();
string poNoString = getVariable(sqlPO).ToString();
string intakeDateString = weekStarting.Day + "/" + weekStarting.Month + "/" + weekStarting.Year + " to " + weekEnding.Day + "/" + weekEnding.Month + "/" + weekEnding.Year;
//adds summary variables to dictionary
Dictionary<string, string> summaryVariablesDict = new Dictionary<string, string>();
summaryVariablesDict.Add("f Week", fWeekString);
//other values added to Dict
//Adds WorkSheets based on above data
if (report.Selected && report.Value.Equals("allserials"))
{
string worksheetName = supplier.Value + " Data " + fWeek.Value;
DataTable dataTable = getDataTable(sqlAllSerials);
createWorkSheet(workbook, worksheetName, dataTable);
}
//Other Reports follow
**//what I hope to do - need this to show in the grid view immediatley not end of method
updateDataTable.Rows.Add(suppler, fweek, "successful");
gridView.DataBind();**
}
}
}
}
}
}
workbook.SaveAs(filePath);
}
}
所以目前这个存在于另一个类中,但是将它移动到aspx页面并没有问题,所以我已经自由地向你展示了我在这个方法中需要做什么。因此,如果它在这方面没有完全意义(即我不会在方法中正常声明网格的数据源)。
我遇到的问题是,在通过回发更新网格视图之前,它将等到方法结束,然后用户立即获取所有内容。我希望有一种方法可以在每次迭代时甚至每隔几秒更新一次gridview,如果我们使用计时器,但无法找到实现这一目的的方法。
长话短说,如何从这种方法更新gridview,其中结果立即出现在用户UI上,而不是等到方法结束。
答案 0 :(得分:0)
我会沿着这些方向做点什么:
如果它有用,我可以提供一些简单的回调样本来帮助你。
编辑:添加了代码示例:
标记:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CallBackWebForm.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript">
var callbackFrequency = 2000;
// Callback javascript
// To make callback to server, call CallServer();
// Receive response from server after callback
function ReceiveServerData(arg, context) {
// Parse the JSON that we got from the server
args = JSON.parse(arg);
// Add rows to table
$.each(args.TableRows, function (index, value) {
$('#table1').append(value);
});
// If we're done, show a message
if (args.DoneLoadingSpreadsheet)
$('#doneDiv').show();
// Otherwise, start a timer to call back again
else
window.setTimeout(function () { CallServer(); }, callbackFrequency);
}
$(document).ready(function() {
// Start the callback loop
window.setTimeout(function () { CallServer(); }, callbackFrequency);
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
Sample page with some progress-y stuff
</div>
<table id="table1">
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
</tr>
<!-- Rows inserted by Javascript will go here -->
</table>
<div id="doneDiv" style="display: none;">
All done!
</div>
</form>
</body>
</html>
代码隐藏:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI;
using Newtonsoft.Json;
namespace CallBackWebForm
{
public partial class Default : System.Web.UI.Page, ICallbackEventHandler
{
protected void Page_Load(object sender, EventArgs e)
{
// Setup Callback javascript so that page can initiate callbacks and receive callback responses
CreateClientSideCallbackFunction();
if (!Page.IsPostBack)
StartBuildingSpreadsheetTask();
}
#region Callback
private void CreateClientSideCallbackFunction()
{
var cm = Page.ClientScript;
// The Javascript function in the markup must exactly match the function name as entered below (ReceiveServerData)
var cbReference = cm.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
// The Javascript function to be placed in the markup which will be used to initiate the callback
var callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
}
/// <summary>
/// Called when CallServer(arg, context) is called in javascript on the page
/// </summary>
/// <param name="eventArgument">Not used, but must be passed</param>
public void RaiseCallbackEvent(string eventArgument)
{
}
/// <summary>
/// Called at the end of a callback; provides the response/result to the client
/// </summary>
/// <returns>JSON string representing an instance of the DataTransferClass</returns>
public string GetCallbackResult()
{
// Serialize the DataTransferObject, then delete all TableRows so we don't send them to the browser again
// Note: this is not currently thread-safe. You should add some sort of locking mechanism so the background thread
// doesn't modify the TableRows list while we're serializing it and deleting from it.
var dtoJson = JsonConvert.SerializeObject(DataTransferObject);
DataTransferObject.TableRows.Clear();
return dtoJson;
}
public class DataTransferClass
{
public bool DoneLoadingSpreadsheet { get; set; }
public List<string> TableRows { get; set; }
}
#endregion Callback
#region Background Task
// Sessions have unique IDs, but individual page views don't. So, create one for this page view.
private string ViewID
{
get
{
if (string.IsNullOrEmpty(ViewState["_viewID"] as string))
ViewState["_viewID"] = Guid.NewGuid().ToString();
return ViewState["_viewID"] as string;
}
}
// Store all DataTransfer data and token sources in static dictionaries so the background task can get to them
private static Dictionary<string, DataTransferClass> DataTransferDictionary = new Dictionary<string, DataTransferClass>();
private static Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
// Make the values in the dictionaries for this View easily accessible via Properties
private DataTransferClass DataTransferObject
{
get
{
if (DataTransferDictionary.ContainsKey(ViewID))
return DataTransferDictionary[ViewID];
else
return null;
}
set
{
if (DataTransferDictionary.ContainsKey(ViewID))
DataTransferDictionary[ViewID] = value;
else
DataTransferDictionary.Add(ViewID, value);
}
}
private CancellationTokenSource TokenSource
{
get
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
return TokenSourcesDictionary[ViewID];
else
return null;
}
set
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
TokenSourcesDictionary[ViewID] = value;
else
TokenSourcesDictionary.Add(ViewID, value);
}
}
private void StartBuildingSpreadsheetTask()
{
DataTransferObject = new DataTransferClass() { DoneLoadingSpreadsheet = false, TableRows = new List<string>() };
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
(new TaskFactory()).StartNew(() => BuildSpreadsheet(ViewID, token), token);
}
private void BuildSpreadsheet(string viewID, CancellationToken token)
{
// Simulate work. Update DataTransferObject every 5 seconds, finish after 30 seconds (6 iterations with 5 second wait);
for (int i = 0; i < 6; i++)
{
// Work for 5 seconds
System.Threading.Thread.Sleep(5000);
// Update DataTransferObject with new row (don't use the 'DataTransferObject' property; it relies up the 'ViewID' property, which in
// turn relies upon ViewState, which isn't available from a background thread).
DataTransferDictionary[viewID].TableRows.Add("<tr><td>Val " + i + "</td><td>Val " + (i * 10) + "</td><td>Val " + (i * 100) + "</td></tr>");
}
// All done; update DataTransferObject
DataTransferDictionary[viewID].DoneLoadingSpreadsheet = true;
}
#endregion Background Task
}
}
一对夫妇注意到:
ICallbackEventHandler
接口编辑2:更新了顶部的建议流程,以匹配我在代码示例中实际执行的操作。