asp.net Web Forms - async await Exception - asp.net

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??

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;
}
}
}

Related

Using Nemiro OAuth library for Google authentication in Webforms

I am trying to use Nemiro library to authenticate against Google in a Webforms asp.net project. This library documentation is at Nemiro GoogleClient Documenation
I have a simple aspx page called ExternalLogin.aspx, whose markup and code-behind are as given below.
Question
With code that I have, when Login using Google button is clicked, then user does not get directed to Google's authorization page. What is missing in my code that is causing this?
Markup
<%# Page Language="C#" AutoEventWireup="true" CodeFile="ExternalLogin.aspx.cs" Inherits="ExternalLogin" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnGoogle" runat="server" Text="Login using Google" OnClick="btnGoogle_Click" />
</div>
</form>
</body>
</html>
Code-behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Nemiro;
using Nemiro.OAuth.Clients;
using Nemiro.OAuth;
public partial class ExternalLogin : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnGoogle_Click(object sender, EventArgs e)
{
var result = OAuthWeb.VerifyAuthorization();
if (result.IsSuccessfully)
{
var user = result.UserInfo;
Response.Write(String.Format("User ID: {0}<br />", user.UserId));
Response.Write(String.Format("Name: {0}<br />", user.DisplayName));
Response.Write(String.Format("Email: {0}", user.Email));
}
}
}
I have also defined the keys for Google OAuth in Application_Start event as below.
void Application_Start(object sender, EventArgs e)
{
Nemiro.OAuth.OAuthManager.RegisterClient(
new Nemiro.OAuth.Clients.GoogleClient(
"some-value-1",
"some-value-2"
));
}
I think you should be looking at OAuthWeb.RedirectToAuthorization method. Here's the API doc for your reference. So just call this method in your btnGoogle_Click, and then verify your authorization in Page_Load event handler.
Here's the sample code:
protected void btnGoogle_Click(object sender, EventArgs e)
{
OAuthWeb.RedirectToAuthorization("Google", new Uri(Request.Url, "ExternalLogin.aspx").AbsoluteUri);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostback)
{
var result = OAuthWeb.VerifyAuthorization();
if (result.IsSuccessfully)
{
var user = result.UserInfo;
Response.Write(String.Format("User ID: {0}<br />", user.UserId));
Response.Write(String.Format("Name: {0}<br />", user.DisplayName));
Response.Write(String.Format("Email: {0}", user.Email));
}
}
}
Also, if you'd like to verify the authorization results on a different page, just change the page name in the URI constructor, and put verification code in the Page_Load event of your new page.
Hope it helps.

Event Bubbling From Web User Controls in ASP.NET

I have two UserControls - UserControl1.ascx and UserControl2.ascx in PageDefault.aspx:
How I can call the method (GetLabelText() in UserControl1.ascx) from UserControl2.ascx using event bubbling?
This is my example code - When I click on the button (UserControl2Button1 in UserControl1.ascx) - I want to call the method GetLabelText() from UserControl2.ascx - using event bubbling.
PageDefault.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="PageDefault.aspx.cs" Inherits="TelerikAjaxEvents.PageDefault" %>
<%# Register TagPrefix="uc" TagName="UserControl1" Src="~/UserControl1.ascx" %>
<%# Register TagPrefix="uc" TagName="UserControl2" Src="~/UserControl2.ascx" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Page Default</title>
</head>
<body>
<form id="form1" runat="server">
UserControl1:
<uc:UserControl1 ID="UserControl1" runat="server" />
UserControl2:
<uc:UserControl2 ID="UserControl2" runat="server" />
</form>
</body>
</html>
UserControl1.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="UserControl1.ascx.cs" Inherits="TelerikAjaxEvents.UserControl1" %>
<asp:Label ID="UserControl1Label1" runat="server"></asp:Label>
UserControl1.ascx.cs
public partial class UserControl1 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void GetLabelText()
{
UserControl1Label1.Text = "Text is Visible";
}
}
UserControl2.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="UserControl2.ascx.cs" Inherits="TelerikAjaxEvents.UserControl2" %>
<asp:Button ID="UserControl2Button1" runat="server" Text="Send"
onclick="UserControl2Button1_Click" />
UserControl2.ascx.cs
public partial class UserControl2 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void UserControl2Button1_Click(object sender, EventArgs e)
{
//Call method from UserControl1 (GetLabelText()) - Show Label text - USING BUBBLE EVENT
}
}
There are many ways to go about this. Mark's answer hints at what was classically known as event bubbling using a built in functionality part of System.Web.UI.Control base control. However, it's a simple exercise to expose your own event, bind to it, and bubble up events through a control hierarchy. For more details on using BubbleEvent in ASP.NET read the following. Please keep in mind that both of these MSDN articles were written for .NET 1.1
Bubbling an Event
Event Bubbling Control Sample
K. Scott Allen does a good job at demonstrating exactly what an "event bubbling" implementation looks like in his post: Event Bubbling From Web User Controls in ASP.NET (C#) . See the following modification to your example for inspiration.
UserControl1 with GetLabelText()
public partial class UserControl1 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void GetLabelText()
{
UserControl1Label1.Text = "Text is Visible";
}
}
UserControl2 with exposed BubbleClick event handler that callers can subscribe to.
public partial class UserControl2 : System.Web.UI.UserControl
{
protected Button UserControl2Button1;
protected void Page_Load(object sender, EventArgs e)
{
}
public event EventHandler BubbleClick;
protected void OnBubbleClick(EventArgs e)
{
if(BubbleClick != null)
{
BubbleClick(this, e);
}
}
protected void UserControl2Button1_Click(object sender, EventArgs e)
{
// do some stuff
OnBubbleClick(e);
}
}
PageDefault subscribes to UserControl2's BubbleClick event handler and executes UserControl1.GetLabelText()
public partial class PageDefault : WebPage
{
UserControl1 userControl1;
UserControl2 userControl2;
protected void Page_Load(object sender, EventArgs e)
{
UserControl2.BubbleClick += RootBubbleClickHandler;
}
protected void RootBubbleClickHandler(object sender, EventArgs e)
{
if (sender is UserControl2)
{
// subscribe UserControl1 to UserControl2 click event and do something
userControl1.GetLabelText();
}
// check for other controls with "BubbleClicks"
}
}
Event Bubbling is a poorly understood concept in ASP.NET WebForms. It does exist (and is used by most of the databound controls), but is often mistaken for the simpler concept of "implement an event in a user control" (as K Scott Allen does).
The core of event bubbling is that the event travels up through the control hierarchy until it is handled or hits the root. This allows some centralization of handler code. It is implemented using the Control.RaiseBubbleEvent and Control.OnBubbleEvent methods. The main use case is controls with a lot of child controls (think Repeaters, ListViews, etc.). Instead of subscribing to each individual control, the Repeater catches them all in it's OnBubbleEvent and raises a single ItemCommandEvent for them.
If you really wanted to use event bubbling (as opposed to K. Scott's example), it'd look something like:
class Page {
override bool OnBubbleEvent(object sender, EventArgs e) {
var btn = sender as Button;
if (btn == null) return false;
// You may want to do further checking that the source is what you want
// You could use CommandEventArgs to do this
this.uc1.GetLabelText();
return true;
}
}
The sequence of events goes like this:
- Button Clicked
- Button RaiseBubbleEvent
- UserControl OnBubbleEvent returns false (default, since you didn't override)
- Page OnBubbleEvent
Can you try declaring the UserControl1 as public property(e.g. "UserControl1") on the PageDefault.aspx and then in the UserControl2, use Parent.Page.UserControl1.GetLabelText()?

ASP.NET how to access public properties?

I have two pages page1.aspx and page2.aspx, both have code behind with partial classes.
How do i access public property message on page1.aspx from page2.aspx ?
public string message { get; set; }
Update
I just read that the one is MasterPage and the other is the client to masterpage ?
then its diferent way.
Page to Page
If you have 2 simple diferent pages.
I have done it this way.
Its a post value, by using asp.net tricks :)
On Page2.aspx add this on top.
<%# PreviousPageType VirtualPath="Page1.aspx" %>
and how I read from Page1.aspx on code behind
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (Page.PreviousPage != null)
{
if(Page.PreviousPage.IsCrossPagePostBack == true)
{
txtGetItFromPreviusPage.Text = PreviousPage.SomeString;
}
}
}
}
On Page1.aspx
the button that send me to Page2.aspx
<asp:Button ID="btnEna" runat="server" Text="Send Some variables to other page"
PostBackUrl="Page2.aspx"
onclick="btnMoveSelection_Click" />
and the code that I use for Page1 calculations or other thinks
public string SomeString
{
set
{
ViewState["txtSomeString"] = value;
}
get
{
if (ViewState["txtSomeString"] != null)
return ViewState["txtSomeString"].ToString();
else
return string.Empty;
}
}
protected void btnMoveSelection_Click(object sender, EventArgs e)
{
// some final calculations
}
If the one is the Master page, and the other is the page that use the master.
The Master Page
<body>
<form id="form1" runat="server">
<div>
<asp:Literal runat="server" ID="txtOnMaster"></asp:Literal>
<br />
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
and the code behind
public partial class Dokimes_StackOverFlow_MasterPage : System.Web.UI.MasterPage
{
public string TextToMaster
{
get { return txtOnMaster.Text; }
set { txtOnMaster.Text = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
// here I find the control in the client page
Control FindMe = ContentPlaceHolder1.FindControl("txtOut");
// and if exist I set the text to client from the master
if (FindMe != null)
{
((Literal)FindMe).Text = "Get from Master Page";
}
}
}
and now the Page1.aspx that have the previus master page
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:Literal runat="server" ID="txtOut"></asp:Literal>
</asp:Content>
and the code
protected void Page_Load(object sender, EventArgs e)
{
// here I set the text on master page from client
((Dokimes_StackOverFlow_MasterPage)Master).TextToMaster = "Set from Client";
}
If you are NOT in a sessionless environment, then in the transmitter page, push your string (or your object - e.g., a Dictionary ) into session:
Session("MyVar") = "WhatEver"
In the receiver page, you can get it back with:
MyPreviousVar = Session("MyVar")
If you want a message property on every page. You could implement your own BasePage and define the message property in your base page. Then derive subsequent pages from your custom base page. That way all of your pages will always have a message property.
However, this isn't going to keep the message property constant through out each page. If you are trying to pass values between pages then you should use either session state or a querystring
This MSDN page may be of use to you.
You shouldn't really be doing this, pages should be standalone entities. If you need to pass this data from one form to another, consider using the querystring, or posting your form to the second page.
OK. Have you tried then Page.Master.Property?

Startup Script Not Firing During Partial Refresh

I have a User Control, named MyUpdateControl, that fires a startup script - its HTML is simply:
<div id="updatableArea"></div>
The startup script is added in the User Control's OnLoad():
string doUpdateScript = String.Format(
"DoUpdate('{0}')",
someValue);
this.Parent.Page.ClientScript.RegisterStartupScript(typeof(Page), "DoUpdateScript", doUpdateScript);
The MyUpdateControl User Control is sometimes contained within an Update Panel in another User Control:
<asp:UpdatePanel ID="myUpdatePanel" runat="server" >
<ContentTemplate>
<UC1:MyUpdateControl ID="myUpdaterControl" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
In those cases, the script is only fired when the page first loads. It never fires during an asynchronous postback. How can I ensure that it's also called during asynchronous postbacks?
You need to use the ScriptManager.RegisterStartupScript when registering a script within an UpdatePanel instead of ClientScript.RegisterStartupScript. There is an overload of this method that expects the control that is registering the client script block as its first parameter.
public class MyUpdateControl : Control
{
public MyUpdateControl()
{
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//..
string doUpdateScript = String.Format(
"DoUpdate('{0}')", someValue);
ScriptManager.RegisterStartupScript(this, GetType(),
"ServerControlScript", script, true);
//..
}
}
The example above uses a Custom Control, i realize you are using a User Control. The two implementations are very similar but i have included an example of this below for completeness.
public partial class MyUpdateControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
//..
string doUpdateScript = String.Format(
"DoUpdate('{0}')", someValue);
ScriptManager.RegisterStartupScript(this, GetType(),
"ServerControlScript", script, true);
//..
}
}

Access Master Page Control

I am using a UserControl Which is present in the Master Page. I need to access a Master page control in the UserControl. I need your suggestions.
The Scenario is A label is present in the Master Page. Based upon selections in the usercontrol i need to modify the masterpage label. The UserControl is present in the Master page itself not in the content place holder.
Create a public method (or public property) in the master page to modify your label and in the UserControl you are able to call it, through the Page.master object:
YourMasterPageClass master = Page.master as YourMasterPageClass;
if(master != null)
{
master.YourEditMethod("hello");
}
Quick and Easy way is to create event in control and handle in master like this:
//Control aspx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="TestControl.ascx.cs"
Inherits="TestControl" %>
<div style="width:300px;border:2px groove blue;">
<asp:Button ID="btn1" runat="server" Text="One" onclick="btn_Click" />
<asp:Button ID="btn2" runat="server" Text="Two" onclick="btn_Click" />
<asp:Button ID="btn3" runat="server" Text="Three" onclick="btn_Click" />
<asp:Button ID="btn4" runat="server" Text="Four" onclick="btn_Click" />
</div>
//Control C#
namespace Controls
{
public partial class TestControl : System.Web.UI.UserControl
{
public delegate void UserChoice(TestEventArgs e);
public event UserChoice OnUserChoice;
protected void btn_Click(object sender, EventArgs e)
{
if (OnUserChoice != null)
OnUserChoice(new TestEventArgs(((Button)sender).Text));
}
}
public class TestEventArgs : EventArgs
{
private string _value;
public TestEventArgs(string str)
{
_value = str;
}
public string Message
{
get { return _value; }
}
}
}
//MasterPage Code
protected void Page_Load(object sender, EventArgs e)
{
test1.OnUserChoice += new
Controls.TestControl.UserChoice(test1_OnUserChoice);
}
void test1_OnUserChoice(ROMS.Intranet.Controls.TestEventArgs e)
{
MasterLabel.Text = e.Message;
}
MasterLabel is name of the label in master page.
test1 is the control in master page.

Resources