c# - 在单独的线程中执行 Action 以解除对 UI 的阻塞

标签 c# asp.net-mvc asp.net-mvc-4

我有一个用于生成报告的表单。我们正在使用 RDLC 报告,报告加载到 aspx 页面中。

这是 Form 的代码,表单目标设置为 _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>
}

这是重定向到 Report aspx 页面的 Controller 操作,在该页面处理和显示 Report。

[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" }))
            {

            <div class="row mt-15">
                <div class="col-md-12 text-center">
                    <input type="submit" class="btn btn-primary" value="Show Report" />
                </div>
            </div>
}

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 加载所有其他页面:

这是执行操作的 Assets 报告页面“显示报告”按钮的图像:

但是一旦单击此按钮,其他 UI 元素将被阻止。例如在生成报告之前,我无法使用 Group Reports 加载 View 。

enter image description here

最佳答案

ASP.NET MVC 倾向于序列化同一 session 的请求,除非您指定该 session 是只读的。

在 Microsoft 的这篇文章中,https://msdn.microsoft.com/en-us/library/ms178581.aspx ,它指出:

Concurrent Requests and Session State

Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.

您可能想考虑将 session 状态设置为只读或使用更明智的技术,如 node.js。 (开玩笑说最后一点……)

请在此处查看 John Culviner 的文章:http://johnculviner.com/asp-net-concurrent-ajax-requests-and-session-state-blocking/ ,他说:

A Solution for MVC 3

Luckily Microsoft has provided ENUM values in System.Web.SessionState.SessionStateBehavior that allow us to give up rights to an exclusive session lock. Mainly the values:

ReadOnly – Doesn’t block other requests because this request can’t update session Disabled – Can’t block, and your best option for performance in StateServer & SQL modes because no serialization needs to occur. Remember that all of session is serialized and de-serialized per user every time. Not just particular keys your are accessing.

Throw a new for MVC 3 attribute with your desired Enum value on your Controller class as such:

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class TestController : Controller
{
    public ActionResult Index()
    {
        System.Threading.Thread.Sleep(10000);
        return new EmptyResult();
    }
}

关于c# - 在单独的线程中执行 Action 以解除对 UI 的阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33276538/

相关文章:

c# - 一页多表单 (MVC3)

c# - MVC MusicStore Artist.Name 对象引用未设置为对象的实例

asp.net - SimpleMembership 向经过身份验证的用户提供登录表单

如果授权失败,Asp.net mvc [Authorize] 属性将返回 http 302 而不是 http 401

c# - 通过 API 发送 IM

c# - 即使有一个以上的空格,也应该只用四个斜杠代替

asp.net-mvc - 在哪里放置审计或日志记录?

c# - 如何使用 asp.net 将 kafka 消息保存到文件中

asp.net-mvc - 如何全局覆盖影响 MVC 中所有 Controller 的方法?

c# - ssis - 美化json字符串