C# asp.net在处理第一个请求时允许单击第二个请求

C# asp.net在处理第一个请求时允许单击第二个请求,c#,asp.net,.net,webforms,C#,Asp.net,.net,Webforms,在Asp.net Web表单应用程序中,我有“生成报告”按钮和“取消”按钮来取消报告生成过程(如果需要很长时间) 当我单击“生成报告”时,它会执行繁重的任务,3秒钟后,我尝试通过单击“取消”按钮取消此繁重的任务 但是服务器端的Cancel按钮click代码会在一段时间延迟后调用 我甚至在JavaScript中尝试了window.stop()来停止页面加载并快速点击服务器代码,但仍然存在延迟 代码: protected void btnExportExcel_Click(object sender

在Asp.net Web表单应用程序中,我有“生成报告”按钮和“取消”按钮来取消报告生成过程(如果需要很长时间)

当我单击“生成报告”时,它会执行繁重的任务,3秒钟后,我尝试通过单击“取消”按钮取消此繁重的任务

但是服务器端的Cancel按钮click代码会在一段时间延迟后调用

我甚至在
JavaScript
中尝试了
window.stop()
来停止页面加载并快速点击服务器代码,但仍然存在延迟

代码:

protected void btnExportExcel_Click(object sender, EventArgs e)
{

// Doing Heavy Task to Generate Report

}


protected void btnCancel_Click(object sender, EventArgs e)
{
    if (Response.IsClientConnected)
    {

       HttpContext.Current.ApplicationInstance.CompleteRequest();

    }
}     

  <asp:Button ID="btnCancel" runat="server" Text="Cancel Request" 

  OnClientClick="return StopPageLoading();" OnClick="btnCancel_Click" />


 function StopPageLoading() {

     try
     {
       window.stop();
     } catch (exception) 
     {
       document.execCommand('Stop'); // for IE and other browsers
     }

  }
protectedvoid btnExportExcel\u单击(对象发送方,事件参数e)
{
//生成报告的繁重任务
}
受保护的无效btnCancel\u单击(对象发送者,事件参数e)
{
if(Response.IsClientConnected)
{
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}     
在Asp.NET中不可能,因为会话状态会产生独占锁

那么如何快速取消呢


使我的方法
async
有助于克服会话状态独占锁问题吗?

是否尝试在代码中使用async/await操作而不是同步操作

当报表生成调用为异步时,一旦报表生成过程启动,就重定向到UI。现在,可以在需要时单击“取消”按钮

这有帮助吗?

正如您所说,您可以这样做

public partial class Contact : Page
{

    private static CancellationTokenSource tokenSource = new CancellationTokenSource();


    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected async void btnExportExcel_Click(object sender, EventArgs e)
    {
        CancellationToken cToken = tokenSource.Token;
        cToken.Register(() => cancelNotification());

        try
        {

            await Task.Run(() =>
            {
                cToken.ThrowIfCancellationRequested();

                GenerateReport(sender, cToken);

            }, cToken);
        }
        catch(OperationCanceledException)
        {

        }
    }

    private void GenerateReport(object sender, CancellationToken ct)
    {
        // Just to Simulate Export to excel

        Thread.Sleep(70000);
    }


    protected void btnCancel_Click(object sender, EventArgs e)
    {

        tokenSource.Cancel();

    }


    private static void cancelNotification()
    {
        // Do your stuff when the user is canceld the report generation

    }
}
在您的
页面.aspx

需要这样的东西吗

<%@ Page Async="true" EnableSessionState="false" %>


我希望这对你有帮助

不过,您可以这样做,也可以不这样做(我不确定),在ASP.NET中创建长时间运行的任务通常不是一个好主意,尤其是如果它们是后台任务。 因此,有一些辅助程序库是用于此目的的

  • 绞刑 Hangfire允许您以一种非常简单但可靠的方式在请求处理管道之外启动方法调用。这些方法调用在后台线程中执行,称为后台作业。 此支持使用取消令牌取消后台作业
  • Quarz.NET 我从未尝试过Quarz.NET
  • 或者你必须自己做。我知道一些项目需要一些专用服务器来完成后台运行和耗时的任务。因此,网站只是一个前端层,设置一些标志来控制任务是否取消
  • 问题出在IIS(或任何其他Web服务器)体系结构中。服务器向客户端(浏览器)返回响应,然后忘记它。您的第一个请求(
    btnextcelexport\u单击
    )必须在返回对浏览器的响应之前完成。单击
    btnCancel
    启动一个新请求,该请求对第一个请求一无所知。
    解决方法是将长作业发送到另一个
    线程
    。可以使用发送到新线程的状态
    object
    与该线程通信。像这样的

    <%@ Page Language="C#" %>
    
    <!DOCTYPE html>
    
    <script runat="server">
    
        protected void btnRun_Click(object sender, EventArgs e)
        {
            var jobState = new StateInfo()
            {
                Id = 1,
                Counter = 0,
                Content = "Start the job",
                Cancelled = false,
                Completed = false
            };
            Session["job"] = jobState; //Save state between requests
            System.Threading.ThreadPool.QueueUserWorkItem(
                new System.Threading.WaitCallback(LongJob),
                jobState
                );//returns immediately
            lblState.Text += "<br />" + jobState.Counter.ToString() 
                + " Completed: " + jobState.Completed.ToString()
                + " Cancelled: " + jobState.Cancelled.ToString()
                + "<br />" + jobState.Content;
            btnCancel.Visible = true;
            btnCheck.Visible = true;
        }
    
        protected void btnCancel_Click(object sender, EventArgs e)
        {
            var jobState = Session["job"] as StateInfo;
            if (!jobState.Completed)
                jobState.Cancelled = true;
            System.Threading.Thread.Sleep(1000);//wait for the next loop to complete
            lblState.Text += "<br />" + jobState.Counter.ToString()
                + " Completed: " + jobState.Completed.ToString()
                + " Cancelled: " + jobState.Cancelled.ToString()
                + (jobState.Completed || jobState.Cancelled ? "<br />" + jobState.Content : "");
        }
    
        protected void btnCheck_Click(object sender, EventArgs e)
        {
            var jobState = Session["job"] as StateInfo;
            lblState.Text += "<br />" + jobState.Counter.ToString()
                + " Completed: " + jobState.Completed.ToString()
                + " Cancelled: " + jobState.Cancelled.ToString()
                + (jobState.Completed || jobState.Cancelled ? "<br />" + jobState.Content : "");
        }
    
        private void LongJob(object state)
        {
            var jobState = state as StateInfo;
            do
            {
                System.Threading.Thread.Sleep(1000);
                jobState.Counter++;
                if (jobState.Counter >= 100)
                {
                    jobState.Completed = true;
                    jobState.Content = "The job is completed";
                }
                else if (jobState.Cancelled)
                    jobState.Content = "The job is cancelled";
            }
            while (!jobState.Cancelled && !jobState.Completed);
        }
        [Serializable]
        class StateInfo
        {
            public int Id { get; set; }
            public int Counter { get; set; }
            public string Content { get; set; }
            public bool Cancelled { get; set; }
            public bool Completed { get; set; }
        }
    
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Long Job</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:Label ID="lblState" runat="server"></asp:Label><br />
                <asp:Button runat="server" ID="btnRun" OnClick="btnRun_Click" Text="Run" />
                <asp:Button runat="server" ID="btnCheck" OnClick="btnCheck_Click" Text="Check State" Visible="false" />
                <asp:Button runat="server" ID="btnCancel" OnClick="btnCancel_Click" Text="Cancel" Visible="false" />
            </div>
        </form>
    </body>
    </html>
    
    
    受保护的无效btnRun\u单击(对象发送方,事件参数e)
    {
    var jobState=new StateInfo()
    {
    Id=1,
    计数器=0,
    Content=“开始作业”,
    取消=错误,
    已完成=错误
    };
    会话[“作业”]=jobState;//在请求之间保存状态
    System.Threading.ThreadPool.QueueUserWorkItem(
    新的System.Threading.WaitCallback(LongJob),
    工作状态
    );//立即返回
    lblState.Text+=“
    ”+jobState.Counter.ToString() +“已完成:”+jobState.Completed.ToString() +“已取消:”+jobState.Cancelled.ToString() +“
    ”+jobState.Content; btnCancel.Visible=真; btnCheck.Visible=true; } 受保护的无效btnCancel\u单击(对象发送者,事件参数e) { var jobState=会话[“作业”]作为StateInfo; 如果(!jobState.Completed) jobState.Cancelled=true; System.Threading.Thread.Sleep(1000);//等待下一个循环完成 lblState.Text+=“
    ”+jobState.Counter.ToString() +“已完成:”+jobState.Completed.ToString() +“已取消:”+jobState.Cancelled.ToString() +(jobState.Completed | | jobState.Cancelled?”
    “+jobState.Content:”); } 受保护的无效b检查\u单击(对象发送者,事件参数e) { var jobState=会话[“作业”]作为StateInfo; lblState.Text+=“
    ”+jobState.Counter.ToString() +“已完成:”+jobState.Completed.ToString() +“已取消:”+jobState.Cancelled.ToString() +(jobState.Completed | | jobState.Cancelled?”
    “+jobState.Content:”); } 私有void LongJob(对象状态) { var jobState=状态为StateInfo; 做 { 系统线程线程睡眠(1000); 计数器++; 如果(jobState.Counter>=100) { jobState.Completed=true; jobState.Content=“作业已完成”; } else if(jobState.Cancelled) jobState.Content=“作业已取消”; } 而(!jobState.Cancelled&&!jobState.Completed); } [可序列化] 类StateInfo { 公共int Id{get;set;} 公共int计数器{get;set;} 公共字符串内容{get;set;} 公共bool已取消{get;set;} 公共bool已完成{get;set;} } 长期工作

    感谢您的写作,我们没有在项目中使用
    async/await
    ,因此我不希望在没有深入了解它的情况下实现它,因为如果没有正确实现,它可能会破坏现有的功能。那么,您有同步请求的解决方案吗?请检查您的报表服务(IIS级别)或SQL连接是否可以