C# 在单独的线程中执行操作以取消阻止UI
我有一个用来生成报告的表单。我们使用的是C# 在单独的线程中执行操作以取消阻止UI,c#,asp.net-mvc,asp.net-mvc-4,C#,Asp.net Mvc,Asp.net Mvc 4,我有一个用来生成报告的表单。我们使用的是RDLC报告,报告加载到aspx页面中 这是表单的代码,表单目标设置为\u blank,并在新选项卡中打开 @using (Html.BeginForm("AssetReports", "AssetReports", FormMethod.Post, new { target = "_blank" })) { <div class="row mt-15"> <div class="col-md-12 text-ce
RDLC
报告,报告加载到aspx
页面中
这是表单
的代码,表单目标设置为\u blank
,并在新选项卡中打开
@using (Html.BeginForm("AssetReports", "AssetReports", FormMethod.Post, new { target = "_blank" }))
{
<div class="row mt-15">
<div class="col-md-12 text-center">
<input type="submit" class="btn btn-primary" value="Show Report" />
</div>
</div>
}
在某些情况下,生成报告需要3,4分钟。在这段时间里,用户界面被阻塞
我们希望在单独的线程上生成报告,以便用户可以在生成报告时使用UI
MVC中有没有一种方法可以在单独的线程中执行此操作
我尝试过使用以下内容,但上下文和会话随后为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" }))
{
<div class="row mt-15">
<div class="col-md-12 text-center">
<input type="submit" class="btn btn-primary" value="Show Report" />
</div>
</div>
}
我使用Ajax加载所有其他页面:
以下是执行操作的资产报告页面“显示报告”按钮的图像:
但一旦单击此按钮,其他UI元素将被阻止。e、 g.在生成报告之前,我无法使用组报告加载视图
在逻辑中添加线程不是运行不“锁定”UI的东西的方法。对ASP.NET MVC应用程序的每个新请求都会自动在一个新线程上提供。如果不是这样,那么多人将无法同时访问您的站点
答案的关键是AJAX。AJAX请求是在后台启动请求的东西
探索将@using(Html.BeginForm)(
替换为@using(Ajax.BeginForm)(
)以在不重新加载页面的情况下发出请求。例如,您还可以使用$jQuery.Ajax()
通过单击按钮启动请求
jQuery.Ajax()
ASP.NET MVC倾向于序列化同一会话的请求,除非指定该会话为只读
在Microsoft的这篇文章中,它指出:
并发请求和会话状态
每个会话对ASP.NET会话状态的访问是独占的,这意味着
如果两个不同的用户同时发出请求,则访问每个用户
同时授予单独的会话。但是,如果两个会话同时
对同一会话发出请求(使用同一会话ID
值),第一个请求以独占方式访问会话
第二个请求仅在第一个请求之后执行
已完成。(如果是独占会话,则第二个会话也可以获得访问权限。)
由于第一个请求超过了
锁定超时。)如果@Page中的EnableSessionState值
指令设置为只读,这是对只读会话的请求
信息不会导致对会话数据的独占锁定。
但是,会话数据的只读请求可能仍需要等待
对于由要清除的会话数据的读写请求设置的锁
您可能希望将会话状态设置为只读,或者使用更合理的技术,如node.js(最后一点只是开个玩笑…)
John Culviner的文章如下所示:
mvc3的一种解决方案
幸运的是,Microsoft在中提供了枚举值
允许我们放弃的System.Web.SessionState.SessionState行为
独占会话锁的权限。主要值:
只读–不阻止其他请求,因为此请求无法
更新会话已禁用–无法阻止,是您的最佳选择
在StateServer和SQL模式下的性能,因为不需要序列化
记住,所有会话都是序列化和反序列化的
每次每个用户。不仅仅是您正在访问的特定密钥
在服务器上抛出一个带有所需枚举值的新for MVC 3属性
控制器类本身:
最后,使用@goofballLogic的答案,我找到了答案
当我使用.aspx
页面在新选项卡中生成报告时,我必须在aspx页面中将会话状态设置为readonly
,而不是控制器
您可以通过将属性EnableSessionState=“ReadOnly”
添加到页面
标记中来完成此操作
<%@ Page Language="C#" EnableSessionState="ReadOnly" AutoEventWireup="true" CodeBehind="ExcessiveIdleReport.aspx.cs"" %>
看看如何使用Ajax.BeginForm,这样您就可以使客户端的调用异步。@JustinHarvey是否可以在新选项卡中打开Ajax表单?使用Form.BeginForm除了呈现标准HTML(如:)之外没有任何特殊功能,因此不应该涉及任何阻塞。您确定页面上没有javascript例程吗在提交表单后,哪一个阻止了交互?@goofballLogic否,它只是一个简单的提交(target=“\u blank”),然后重定向到aspx页面。@重定向后的goofballLogic主页被阻止,直到生成报告。您是说Ajax.BeginForm吗?是否可以在新选项卡中打开Ajax表单?我不想在同一选项卡/窗口中显示报告您可以通过向表单..我将添加一个示例在生成报告之前这仍然会阻塞UI编辑可能是因为您需要安装相关的Nuget软件包(如果您还没有),以支持Ajax.BeginForm或只是,直到那时它将像普通表单一样运行。谢谢。我将对此进行研究,并了解其进展情况。我很高兴您能够成功访问最后!我不知道会话状态/只读,所以它教会了我一些新的东西。谢谢!也适用于crystal reports。谢谢。
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';
}
$(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
{
<div class="row mt-15">
<div class="col-md-12 text-center">
<input type="submit" class="btn btn-primary" value="Show Report" />
</div>
</div>
}
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
}
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class TestController : Controller
{
public ActionResult Index()
{
System.Threading.Thread.Sleep(10000);
return new EmptyResult();
}
}
<%@ Page Language="C#" EnableSessionState="ReadOnly" AutoEventWireup="true" CodeBehind="ExcessiveIdleReport.aspx.cs"" %>