I have a form which is used to generate a Report. We are using RDLC
reports and the Report is loaded in an aspx
page.
So this is the code for the Form
, form target is set to _blank
, and opens in new Tab.
@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>
}
This is the Controller action which redirects to the Report aspx page, where the Report is processed and displayed.
[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;
}
}
The Reports take 3,4 minutes to generate in some cases. and during this time the UI is blocked,
We want the report to generate on a separate thread so that user can use the UI while the report is generated.
Is there a way in MVC C# to Execute this Action in a separate Thread?
I have tried using the following, but the Context and Session are then NULL
Task.Factory.StartNew(() =>
{
switch (model.SelectedReportType)
{
case AssetReportTypesEnum.ExcessiveIdleReport:
Response.Redirect("~/Reports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx");
break;
}
});
and also:
new Thread(() =>
{
switch (model.SelectedReportType)
{
case AssetReportTypesEnum.ExcessiveIdleReport:
Response.Redirect("~/Reports/AssetReports/ExcessiveIdleReport/ExcessiveIdleReport.aspx");
break;
}
}).Start();
EDIT
Code to generate the Report in - This is the code that takes 3 to 4 minutes 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;
}
}
}
}
I have also tried using 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';
}
I load all other Pages using Ajax:
Here is an image of the Asset Reports page 'Show Report' button which executes the action:
But once this button is clicked other UI Elements are Blocked. e.g. I can't load View with Group Reports
until the Report has been generated.
ASP.NET MVC tends to serialize requests for the same session unless you specify that session is readonly.
In this article by Microsoft, https://msdn.microsoft.com/en-us/library/ms178581.aspx, it states:
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.
You might want to look into making the session state readonly or use a more sensible technology like node.js. (just kidding about the last bit...)
See here for John Culviner's article: http://johnculviner.com/asp-net-concurrent-ajax-requests-and-session-state-blocking/, where he says:
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();
}
}
Adding threads into your logic isn't the way to run something that doesn't 'lock' the UI. Each new request to your ASP.NET MVC application is automatically served on a new thread. If this wasn't the case then multiple people wouldn't be able to visit your site at the same time.
The key to your answer is AJAX. An AJAX request is something that kicks off a request in the background.
Explore replacing your @using (Html.BeginForm(
with @using (Ajax.BeginForm(
to make your request without reloading the page. You can also use $jQuery.ajax()
to kick your request off from a button click for example.
$(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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With