【问题标题】:asp.net Web Forms - async await Exceptionasp.net Web 表单 - 异步等待异常
【发布时间】:2019-06-19 19:11:45
【问题描述】:

全部,

我正在使用 asp.net 网络表单,asp.net 4.7.2。 注意:页面标记为

Async="true"

我创建了一个具有事件的用户控件(在我的示例中:ProcessOrder 事件)。当用户控件尝试引发事件时

this.ProcessOrder?.Invoke(this, args);

我立即得到这个异常:

System.InvalidOperationException: '此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行 Page 时发生此异常,请确保将 Page 标记为 。此异常还可能表示尝试调用“async void”方法,在 ASP.NET 请求处理中通常不支持该方法。相反,异步方法应该返回一个任务,调用者应该等待它。'

页面上的事件处理方法签名为:

protected async void PaymentOptions_ProcessOrder(object sender, PaymentOptions.ProcessOrderEventArgs e)

并执行这一行:

await _SubmitOrderAsync(e.PaymentToken, e.PaymentTokenDescriptor);

依次执行我的发送异步电子邮件方法

await this.CheckoutServices.TrySendOrderConfirmationEmailAsync(receipt);

就像我提到的那样,我标记了页面 Async 并且我确实遵循了异步方法的协议,但不确定问题是什么。

更新:

我对代码做了一些改动,我从 PaymentOptions_ProcessOrder 事件处理程序中删除了 async 关键字,因此我不再等待 _SubmitOrderAync() 方法(这很好,因为它后面没有代码)。但是,在 SubmitOrderAsync() 方法中

private async void _SubmitOrderAsync(string paymentToken, string paymentTokenDescriptor)

我正在等待 TrySendOrderConfirmationEmailAsync()

await this.CheckoutServices.TrySendOrderConfirmationEmailAsync(receipt);

现在,当我运行此代码时,它会在调用 _SubmitOrderAsync() 时出现相同的异常(它基本上在使用 async 关键字修饰的第一个方法上出错)。现在不确定,我所有可等待的方法都返回一个任务,但我不再等待的 _SubmitOrderAsync() 除外。

更新 2

好的,为了进一步解决这个问题,我在同一页面上创建了一个虚拟按钮并为其创建了一个异步 onclick 事件处理程序

protected async void btnGO_Click(object sender, EventArgs e)

我放置了我试图在其中运行的异步方法 TrySendOrderConfirmationEmailAsync() 并且它有效! 按钮单击事件处理程序的调用方式与我的用户控件的事件处理程序有什么区别????同样,我使用这一行来调用用户控件中的事件处理程序:

this.ProcessOrder?.Invoke(this, args);

我应该使用不同的线路吗?

【问题讨论】:

  • 放置在新的 ASP.NET 4.6.1 WebForms 项目中是否有效?
  • 查看我的更新 2,异步代码从该页面运行良好。
  • 不等待异步方法调用是不行的。如果那些失败而你不等待,你就不会知道他们失败了。这种情况很难调试。如果您进行异步调用,请确保等待它。
  • 我知道,如您所见,发布的初始示例等待调用链上的所有异步调用。我一直在更改代码以查看为什么事情不起作用。这就是我需要帮助的地方。

标签: asp.net webforms async-await


【解决方案1】:

根据documentation,执行异步代码的唯一方法是使用页面异步任务。

这是一个工作示例:

默认.aspx:

<%@ Page Language="C#" AutoEventWireup="True" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" Async="True" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <p>
                <asp:Label ID="IsPostBackLabel" runat="server" Text=""></asp:Label>
            </p>
            <p>
                <asp:Button ID="Button1" runat="server" Text="Button 1" OnClick="Button1_Click" /><asp:Label ID="IsButton1Label" runat="server" Text="Clicked!"></asp:Label>
            </p>
            <p>
                <asp:Button ID="Button2" runat="server" Text="Button 2" OnClick="Button2_Click" /><asp:Label ID="IsButton2Label" runat="server" Text="Clicked!"></asp:Label>
            </p>
            <p>
                <asp:Button ID="Button3" runat="server" Text="Button 3" OnClick="Button3_Click" /><asp:Label ID="IsButton3Label" runat="server" Text="Clicked!"></asp:Label>
            </p>
        </div>
    </form>
</body>
</html>

默认.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class Default : System.Web.UI.Page
    {
        private bool isPostBack;
        private int buttonclicked;

        protected override void OnLoad(EventArgs e)
        {
            this.RegisterAsyncTask(
                new PageAsyncTask(
                    this.IsPostBack
                        ? new Func<Task>(OnPostBackAsync)
                        : new Func<Task>(OnNotPostBackAsync)));
        }

        protected override void OnPreRenderComplete(EventArgs e)
        {
            base.OnPreRender(e);

            this.IsPostBackLabel.Text = this.isPostBack
                ? "This was a post back!"
                : "This was not a post back!";

            this.IsButton1Label.Visible = this.buttonclicked == 1;
            this.IsButton2Label.Visible = this.buttonclicked == 2;
            this.IsButton3Label.Visible = this.buttonclicked == 3;
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            this.RegisterAsyncTask(
                new PageAsyncTask(
                    () => OnButtonClickedAsync(1)));
        }

        protected void Button2_Click(object sender, EventArgs e)
        {
            this.RegisterAsyncTask(
                new PageAsyncTask(
                    () => OnButtonClickedAsync(2)));
        }

        protected void Button3_Click(object sender, EventArgs e)
        {
            this.RegisterAsyncTask(
                new PageAsyncTask(
                    () => OnButtonClickedAsync(3)));
        }

        private async Task OnPostBackAsync()
        {
            await Task.Delay(1).ConfigureAwait(false);

            this.isPostBack = true;
        }

        private async Task OnNotPostBackAsync()
        {
            await Task.Delay(1).ConfigureAwait(false);

            this.isPostBack = false;
        }

        private async Task OnButtonClickedAsync(int button)
        {
            await Task.Delay(1).ConfigureAwait(false);

            this.buttonclicked = button;
        }
    }
}

【讨论】:

  • 这不是真的,因为 void 异步事件处理程序在 asp.net webforms 中工作得很好,请参阅我的更新 2。我确实看过这篇文章,顺便说一下,R​​egisterAsyncTask() 对我来说完全没用仅当您在 Page_Load 中注册任务时它才会起作用,并且不能在回发时完成。
  • 感谢您的示例,我会尝试一下,有趣的是,在我的代码库中,回发的 RegisterAsyncTask() 什么也没做。无论哪种方式,这个例子都没有回答我在 async/await 中面临的实际问题,这只是一种解决方法。
  • 不!这是执行 Web 表单的正确方法,也是在 Web 表单上执行异步工作的正确方法。
  • 那么这种方法有什么问题呢? protected async void MyButton_Click()
  • 您永远无法知道async void 方法何时结束。 Windows FormsWeb Forms 之间的相似之处几乎只停留在 Forms 上。 Windows Forms 应用程序的运行时间比 Web Forms 请求长,后者具有非常具体和明确的生命周期。到 PreRenderComplete 发生时,一切都必须为渲染阶段做好准备。您能指出我推荐您所做的任何 Microsoft 来源吗?
猜你喜欢
  • 2015-02-03
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多