0

All,

I am using asp.net web forms, asp.net 4.7.2. NOTE: The page is marked as

Async="true"

I have created a user control that has an event (in my example: ProcessOrder event). When the user control tries to raise the event

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

I immediately get this exception:

System.InvalidOperationException: 'An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.'

The event handler method signature on the page is:

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

and executes this line:

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

which in turn executes my send async email method

await this.CheckoutServices.TrySendOrderConfirmationEmailAsync(receipt);

Like I mentioned, I marked the page Async and I did follow the protocol for async methods, not sure what the problem is.

UPDATE:

I made slight change to the code, I removed async keyword from PaymentOptions_ProcessOrder event handler and therefore I am NOT awaiting _SubmitOrderAync() method anymore (which is fine since there's no code after it). But, in the SubmitOrderAsync() method

private async void _SubmitOrderAsync(string paymentToken, string paymentTokenDescriptor)

I am awaiting TrySendOrderConfirmationEmailAsync()

await this.CheckoutServices.TrySendOrderConfirmationEmailAsync(receipt);

Now, when I run this code, it blows up with the same exception when _SubmitOrderAsync() is invoked (it basically errors out on the first method decorated with async keyword). Not sure now, I have all my methods that are awaitable return a task with the exception of _SubmitOrderAsync() which I am not awaiting anymore.

UPDATE 2

OK, to troubleshoot this problem further, I created a dummy button on the very same page and created an async onclick event handler for it

protected async void btnGO_Click(object sender, EventArgs e)

I placed my async method TrySendOrderConfirmationEmailAsync() that I am trying to run in there and it works! What is the difference between how the button click event handler is invoked vs my user control's event handler???? Again, I am using this line to invoke my event handler in the User Control:

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

Should I be using different line??

4
  • Does it work when placed in a new ASP.NET 4.6.1 WebForms project? Commented Jun 19, 2019 at 19:31
  • See my Update 2, async code runs fine from that very page. Commented Jun 19, 2019 at 20:19
  • It's not okay to not await async method calls. If those fail and you don't await, you won't know they've failed. That kind of situation can be extremely hard to debug. If you make an async call, make sure you await it. Commented Jun 19, 2019 at 20:52
  • I know, and as you can see, the initial example posted, awaits all async calls up the call chain. I've been just changing code to see why things are not working. That's what I need help with. Commented Jun 19, 2019 at 20:58

1 Answer 1

1

According to the documentation, the only way to execute asynchronous code is using page asynchronous tasks.

Here's a working example:

Default.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>

Default.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;
        }
    }
}
Sign up to request clarification or add additional context in comments.

12 Comments

That's not true, since void async event handlers works just fine in asp.net webforms, see my Update 2. I did look at the article, and btw the RegisterAsyncTask() is completely useless to me since it will only work if you register the task in Page_Load and it must be done NOT on a postback.
Thanks for your example, I'll try it out, interestingly, in my code base, RegisterAsyncTask() on postback did nothing. Either way, this example doesn't answer the actual problem I am facing with async/await, this is just a work around.
No! this is the right way to do Web Forms and the right way to do async work on Web Forms.
What is wrong with this approach then? protected async void MyButton_Click() <= It is used in WinForms and WebForm and MS sources I looked don't discourage this practice depending what you're trying to do. I find async void handler much cleaner solution in certain situations then registering async tasks.
You can never know when an async void method will end. The similarities between Windows Forms and Web Forms almost stops at Forms. A Windows Forms application runs for longer than a Web Forms request, which has a very specific and defined life cycle. By the time PreRenderComplete occours, everything must be ready for the rendering phase. Can you point me to any Microsoft source that recommends what you did?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.