在单独的线程中执行Action以取消阻止UI
我有一个用于生成报告的表单。 我们正在使用RDLC
报告,报告加载到aspx
页面中。
因此,这是Form
的代码,表单目标设置为_blank
,并在新选项卡中打开。
@using (Html.BeginForm("AssetReports", "AssetReports", FormMethod.Post, new { target = "_blank" })) { }
这是Controller操作,它重定向到Report aspx页面,处理和显示报告。
[HttpPost] public void AssetReports(AssetReportsDTO model, AssetReportParametersDTO reportParameters) { SessionHandler.AssetReport = model; SessionHandler.AssetReportParameters = reportParameters; switch (model.SelectedReportType) { case AssetReportTypesEnum.ExcessiveIdleReport: Response.Redirect("~/Reports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx"); break; } }
在某些情况下,报告需要3,4分钟才能生成。 在此期间,UI被阻止,
我们希望报告在单独的线程上生成,以便用户可以在生成报告时使用UI。
在MVC C#中是否有办法在单独的线程中执行此操作?
我已尝试使用以下内容,但Context和Session NULL
Task.Factory.StartNew(() => { switch (model.SelectedReportType) { case AssetReportTypesEnum.ExcessiveIdleReport: Response.Redirect("~/Reports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx"); break; } });
并且:
new Thread(() => { switch (model.SelectedReportType) { case AssetReportTypesEnum.ExcessiveIdleReport: Response.Redirect("~/Reports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx"); break; } }).Start();
编辑
用于生成报告的代码 – 这是需要3到4分钟的代码ExcessiveIdleReport.aspx
public partial class ExcessiveIdleReport1 : Page { private IReportsProvider _reportsProvider; protected void Page_Load(object sender, EventArgs e) { _reportsProvider = new ReportsProvider(); if (!IsPostBack) { try { var reportDetails = SessionHandler.AssetReport; var reportParams = SessionHandler.AssetReportParameters; var sPath = Server.MapPath("../ExcessiveIdleReport/ExcessiveIdleReport.rdlc"); var dsExcessiveReport = _reportsProvider.GetExcessiveIdleReport(reportDetails.CompanyId, reportDetails.AssetId, reportDetails.StartDate, reportDetails.EndDate, reportParams.SelectedIdleTime * 60); ExcessiveIdleReportViewer.ProcessingMode = ProcessingMode.Local; ExcessiveIdleReportViewer.LocalReport.EnableHyperlinks = true; ExcessiveIdleReportViewer.HyperlinkTarget = "_blank"; ExcessiveIdleReportViewer.LocalReport.DataSources.Add(new ReportDataSource("ExcessiveIdleReport", dsExcessiveReport.Tables[0])); ExcessiveIdleReportViewer.LocalReport.DataSources.Add(new ReportDataSource("ReportHeaderDetails", dsExcessiveReport.Tables[1])); ExcessiveIdleReportViewer.LocalReport.DataSources.Add(new ReportDataSource("ReportSummary", dsExcessiveReport.Tables[2])); ExcessiveIdleReportViewer.LocalReport.ReportPath = sPath; ExcessiveIdleReportViewer.LocalReport.EnableExternalImages = true; ExcessiveIdleReportViewer.LocalReport.SetParameters(param); ExcessiveIdleReportViewer.LocalReport.Refresh(); } catch (Exception ex) { ErrorDiv.InnerText = string.Format("An error occured while generating the ExcessiveIdleReport, Please contact Support with following Message: [{0}] - [{1}]", ex.Message, ex.StackTrace); ReportContentDiv.Visible = false; ErrorDiv.Visible = true; } } } }
我也尝试过使用Ajax.BeginForm
@using (Ajax.BeginForm("AssetReports", "AssetReports", new AjaxOptions() { HttpMethod = "POST", OnSuccess = "OpenReport"}, new { target = "_blank" })) { }
JS:
function OpenReport(response) { var popup = window.open("about:blank", "_blank"); // the about:blank is to please Chrome, and _blank to please Firefox popup.location = '/TBReports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx'; }
我使用Ajax加载所有其他页面:
以下是资产报告页面“显示报告”按钮的图像,该按钮执行操作:
但是,单击此按钮后,其他UI元素将被阻止。 例如,在生成Group Reports
之前,我无法加载带有Group Reports
视图。
ASP.NET MVC倾向于序列化同一会话的请求,除非您指定该会话是只读的。
在Microsoft的这篇文章中, https : //msdn.microsoft.com/en-us/library/ms178581.aspx中指出:
并发请求和会话状态
对会话状态的访问是每个会话独占的,这意味着如果两个不同的用户发出并发请求,则同时授予对每个单独会话的访问权限。 但是,如果对同一会话发出两个并发请求(通过使用相同的SessionID值),则第一个请求将获得对会话信息的独占访问权。 第二个请求仅在第一个请求完成后执行。 (如果由于第一个请求超过锁定超时而释放信息的独占锁定,则第二个会话也可以访问。)如果@ Page指令中的EnableSessionState值设置为ReadOnly,则只读请求会话信息不会导致会话数据的独占锁定。 但是,会话数据的只读请求可能仍然必须等待由会话数据的读写请求设置的锁定才能清除。
您可能希望将会话状态设置为只读或使用更合理的技术,如node.js. (开玩笑说最后一点……)
请参阅John Culviner的文章: http ://johnculviner.com/asp-net-concurrent-ajax-requests-and-session-state-blocking/,他说:
MVC 3的解决方案
幸运的是,Microsoft在System.Web.SessionState.SessionStateBehavior中提供了ENUM值,允许我们放弃对独占会话锁的权限。 主要是价值观:
ReadOnly – 不阻止其他请求,因为此请求无法更新会话已禁用 – 无法阻止,并且在StateServer和SQL模式下性能的最佳选择,因为不需要进行序列化。 请记住,每次会话都会对所有会话进行序列化和反序列化。 不只是您正在访问的特定密钥。
在Controller类上为您的所需Enum值抛出一个新的MVC 3属性,如下所示:
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] public class TestController : Controller { public ActionResult Index() { System.Threading.Thread.Sleep(10000); return new EmptyResult(); } }
在您的逻辑中添加线程不是运行不“锁定”UI的方法。 对ASP.NET MVC应用程序的每个新请求都会自动在新线程上提供。 如果情况并非如此,那么多人将无法同时访问您的网站。
答案的关键是AJAX。 AJAX请求是在后台启动请求的东西。
探索替换你的@using (Html.BeginForm(
使用@using (Ajax.BeginForm(
无需重新加载页面即可发出请求。例如,你也可以使用$jQuery.ajax()
来点击按钮。)
jQuery.Ajax()
$(document).delegate('#myForm', 'submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', dataType: 'json', url: '/AssetReports', data: $('#myForm').serialize(), // Post data from form success: function (responseData) { // Perform redirect to report? }, error: function (jqXHR, textStatus, errorThrown) { // Display error? } }) }); @using (Html.BeginForm("AssetReports", "AssetReports", FormMethod.Post, new { target = "_blank", id = "myForm" })) // Added id { }
或者Ajax.BeginForm
function OpenReport(data, status, xhr) { // Open the report in a new window window.open("\\link\to\report"); } @using (Ajax.BeginForm("AssetReports", "AssetReports", null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "OpenReport" }, new { target = "_blank" })) { // ... Form }
所以最后使用@goofballLogic的答案我找到了答案。
当我使用.aspx
页面在New Tab中生成报告时,我不得不在aspx页面而不是Controller中将会话状态设置为readonly
。
您可以通过将属性EnableSessionState="ReadOnly"
到Page
标记来完成此操作
<%@ Page Language="C#" EnableSessionState="ReadOnly" AutoEventWireup="true" CodeBehind="ExcessiveIdleReport.aspx.cs"" %>
如果您使用Ajax.BeginForm
或Html.BeginForm
,这并不重要。