Reputation: 1166
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??
Upvotes: 0
Views: 3322
Reputation: 14846
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;
}
}
}
Upvotes: 1