I'm attempting to use the new ASP.NET Identity 2.0 authentication system(s) in a WebForms application, but I'm having trouble validating a user before allowing the data source for users to save.
The trouble stems from calling IIdentityValidator.ValidateAsync from the data source's OnUpdating event. The markup is functionally identical to the default Dynamic Data templates (except for the addition of Async="true"), with a few customizations in the code behind. Basically, I manually set the MetaTable for the request (since this page is a replacement for one of my dynamic data routes, but I'd like to keep the benefit of scaffolded properties) and I've added the DetailsDataSource_Updating event. Though the code sample below successfully saves the user to our database, the following error is usually thrown before returning to the client:
"An asynchronous module or handler completed while an asynchronous operation was still pending."
I've spent a considerable amount of time attempting to get this to work, but have yet to find a solution that does not lock up the page or throw the above error. I fear that I am completely misunderstanding async/await in WebForms, or worse, that async/await is only really usable for database queries/binding outside of MVC.
public partial class Edit : System.Web.UI.Page
{
protected UserManager manager;
protected CustomMetaTable table;
protected void Page_Init(object sender, EventArgs e)
{
manager = UserManager.GetManager(Context.GetOwinContext());
table = Global.DefaultModel.GetTable(typeof(User)) as CustomMetaTable;
DynamicDataRouteHandler.SetRequestMetaTable(Context, table);
FormView1.SetMetaTable(table);
DetailsDataSource.EntityTypeFilter = table.EntityType.Name;
}
protected void Page_Load(object sender, EventArgs e)
{
Title = table.EntityName;
DetailsDataSource.Include = table.ForeignKeyColumnsNames;
}
protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
{
if (e.CommandName == DataControlCommands.CancelCommandName)
{
Response.Redirect(table.ListActionPath);
}
}
protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
if (e.Exception == null || e.ExceptionHandled)
{
Response.Redirect(table.ListActionPath);
}
}
protected async void DetailsDataSource_Updating(object sender, Microsoft.AspNet.EntityDataSource.EntityDataSourceChangingEventArgs e)
{
IdentityResult result = await manager.UserValidator.ValidateAsync(e.Entity as User);
if (!result.Succeeded)
{
e.Cancel = true;
}
}
In the process of writing a new UserValidator with a synchronous Validate method, I found a class in the Identity assembly which is used in all the synchronous wrappers for UserManager and RoleManager. I copied this class into my project and it has allowed me to consume async methods synchronously with only a few exceptions (the primary exception seems to be avoided by assigning the result to a variable before referencing it elsewhere).
internal static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
Usage:
AsyncHelper.RunSync(() => manager.UserValidator.ValidateAsync(e.Entity as User));
Related
I have a legacy Windows Forms application that I am working on, I made some changes to the http client, I wanted to make it a singleton so that it could be reused throughout the application. It seems to be causing a deadlock.
I am going to paste all the code that I believe is involved below:
This is the calling code where the UI gets frozen, it never unfreezes.
private async void lbGroup_SelectedIndexChanged_1(object sender, EventArgs e)
{
int groupId = this.lbGroup.SelectedIndex + 1;
await LoadStores(groupId);
//The code below freezes the application
this.lbStore.DataSource = _stores;
this.txtSearch.Enabled = true;
this.lbStore.Enabled = true;
}
This is the LoadStores Method where the httpClient is used:
private async Task LoadStores(int group)
{
try
{
HttpResponseMessage res = await _httpClient.GetAsync("api/GetStoresByGroup/" + group.ToString());
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
var serializedStores = await res.Content.ReadAsStringAsync();
_stores = JsonConvert.DeserializeObject<IEnumerable<Store>>(serializedStores).Select(s => s.StoreName).ToList();
res.Content.Dispose();
}
}
catch (Exception ex)
{
ErrorLogger.LogError("Installation", $"Error getting stores list: {ex.Message}");
}
}
This is the Http Singleton Class:
public static class HttpClientSingleton
{
private static readonly HttpClient _instance;
static HttpClientSingleton()
{
_instance = new HttpClient();
_instance.BaseAddress = new Uri("https://www.i-city.co.za/");
_instance.DefaultRequestHeaders.Accept.Clear();
_instance.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
public static HttpClient Instance
{
get
{
return _instance;
}
}
}
This is the form constructor where the HttpClient gets initiliazed:
public partial class frmInstallationHelper : Form
{
private static string _configDir;
private static string _localConfigDir;
private static int _storeID;
private static Activation _activation;
private static HttpClient _httpClient = HttpClientSingleton.Instance;
private static IEnumerable<string> _stores;
private static IEnumerable<string> _franchisees;
private int _smsCounter;
If I wrap the http request in a using statement inside of the LoadStores method, the app runs fine, but I don't want to dispose of the http Client as that defeats the purpose of making it a singleton.
Update: Problem Found
After following #MongZhu's lead I replicated the program and confirmed that none of the above code was actually causing the deadlock. It was caused by another method that was triggered by the lbStore list Box onSelectChange event displayd below:
private void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
LoadFranchisees(store).Wait();
this.lbFranchisees.DataSource = _franchisees;
}
The way I solved the problem was by changing it to look as follows:
private async void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
await LoadFranchisees(store);
this.lbFranchisees.DataSource = _franchisees;
}
I was busy changing all the .wait() methods to async / await, and I must have forgotten this one.
The deadlock arises because you used Wait in a method which was triggered by an async opertaion. Unfortunately it was masked very good by the apparent hanging in the line of the initialization of the DataSource. But this initialization triggered the SelectedIndexChanged of the listbox which had the evil Wait call in it. Making this method async and await the result will evaporate the deadlock.
private async void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
_franchisees = await LoadFranchisees(store);
this.lbFranchisees.DataSource = _franchisees;
}
I would suggest to return the stores directly from the method instead of using a class variable as transmitter. This way you would also avoid race conditions (to which methods that use class variables are very much prone) If you need it further you could store the returning value inside the _stores variable. But a loading method should rather return the results instead of secretely storing it somewhere hidden from the user of this method.
private async Task<List<Store>> LoadStores(int group)
{
try
{
HttpResponseMessage res = await _httpClient.GetAsync("api/GetStoresByGroup/" + group.ToString()))
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
var serializedStores = await res.Content.ReadAsStringAsync();
res.Content.Dispose();
return JsonConvert.DeserializeObject<IEnumerable<Store>>(serializedStores).Select(s => s.StoreName).ToList();
}
}
catch (Exception ex)
{
ErrorLogger.LogError("Installation", $"Error getting stores list: {ex.Message}");
}
}
You can await the result in the event:
private async void lbGroup_SelectedIndexChanged_1(object sender, EventArgs e)
{
int groupId = this.lbGroup.SelectedIndex + 1;
_stores = await LoadStores(groupId);
this.lbStore.DataSource = _stores;
this.txtSearch.Enabled = true;
this.lbStore.Enabled = true;
}
The same logic applies to the LoadFranchisees method, refactor it so that it returns the data. This makes your code much more understandable. Don't hide information from the reader of a method. It could be you in 6 Month trying to figure out what da heck you did there.... Be nice to your future self at least ;)
I have the 3 methods below in a razor file
protected override async Task OnInitializedAsync()
{
EditContext = new EditContext(_projectModel);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
}
This method is an async method and I have to await it anywhere it is been called
private async Task SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
await JsRuntime.InvokeVoidAsync("Animate");
}
else
{
OkayDisabled = "disabled";
}
}
I am using the EditContext for validation in a Blazor server application.
I have been getting the error message on this line below in the OnInitializedAsync() method and not sure how to proceed with it.
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
Error Message:
Task MyProject.EditContext_OnFieldChanged(object, FieldChangedEventArgs)'
has the wrong return type.
Expected a method with void EditContext_OnFieldChanged(object?, FieldChangedEventArgs e)
Please note that I am using sonarqube to check all my code.
You can assign an async lambda to the event handler, like this:
EditContext.OnFieldChanged +=
async (sender,args) => await EditContext_OnFieldChanged(sender,args);
But, you should be aware that the EditContext/Form will not await your task. Anything you do in that async task will be out of sync with the editcontext.
You should probably include a cancellation token in your async code as well, so that multiple changes to a field do not fire multiple validation tasks at the same time.
Async validation is hard - make sure you test every possible scenario.
Generated Blazor eventhandlers (like #onclick="...") are flexible about return type and parameters but EditContext.OnFieldChanged is not, it has a fixed delegate type.
Make the following change:
//private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
private async void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
StateHasChanged(); // make sure OkayDisabled takes effect
}
On another note, you can probably make OkayDisabled a boolean and use disabled="#OkayDisabled" where you need it.
Blazor makes the disabled attribute disappear when you assign it with false.
Alternative: keep the validation synchronous. That might prevent some problems as #Mister Magoo points out. And then let only the Animation run async.
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
SetOkDisabledStatus();
}
private void SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
_ = JsRuntime.InvokeVoidAsync("Animate"); // no await, on purpose
}
else
{
OkayDisabled = "disabled";
}
}
StateHasChanged() should not be needed in this scenario.
I'm working in an old project (web forms). I'm adding a new module to the system. Specifically, enable and disable roles massively.
In the web form RoleAssignment, I have methods that differ depending on the operation to be performed (enable or disable). So, I'm using switch-case and if-else blocks to know what type of operation is selected.
I would like to decouple that functionality to not use switch-case blocks.
Well, what I've tried is create a class Operation that has methods like LoadUsers(), ExecuteAction(), etc. Then, two classes Enabling and Disabling that inherit from Operation like this:
public abstract class Operation : RoleAssignment
{
public abstract void LoadUsers();
public abstract void ExecuteAction();
}
public class Enabling: Operation
{
protected void LoadUsers()
{
//Calling sp that load users who don't have the selected role
}
protected void ExecuteAction()
{
...
}
}
public class Disabling: Operation
{
protected void LoadUsers()
{
//Calling sp that load users who have the selected role
}
protected void ExecuteAction()
{
...
}
}
Currently my web form is (summarized):
public partial class RoleAssignment: Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoadUsers();
}
private void LoadUsers()
{
//This is what I intend to eliminate
switch(ddlOperation.SelectedValue)
{
case "enable": break;
case "disable": break;
}
}
...
}
I tried change the web form to this, but it didn't work:
public partial class RoleAssignment: Page
{
private Operation operation;
protected void Page_Load(object sender, EventArgs e)
{
//This would be the only switch-case block that I would use
switch(ddlOperation.SelectedValue)
{
case "enable": operation = new Enabling(); break;
case "disable": operation = new Disabling(); break;
}
LoadUsers();
}
private void LoadUsers()
{
operation.LoadUsers(); //No need to use switch-case block
}
private void ExecuteAction()
{
operation.ExecuteAction(); //No need to use switch-case block
}
...
}
The problem is when it creates an instance of Operation, it creates a new Context of RoleAsignment I mean, all variables are null.
I don't know if you understand what I want to do and if exist a simple way to solve this.
The common description of why async void is part of C# is for event handlers. For example:
private async void button_Click(object sender, RoutedEventArgs e)
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://example.com");
var content = await response.Content.ReadAsStringAsync();
this.textBox.Text = content;
}
}
I find this reason unsatisfying, as this kind of event handler can be written without async void like this:
private void button_Click(object sender, RoutedEventArgs e)
{
button_ClickAsync().ForgetTask();
}
private async Task button_ClickAsync()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://example.com");
var content = await response.Content.ReadAsStringAsync();
this.textBox.Text = content;
}
}
static class TaskExtensions { public static void ForgetTask(this Task task) { } }
Why isn't the latter good enough? Why is async void a necessary part of C#? What problem can't be solved without async void?
As you showed yourself, it's not necessary. You can write a functionally identical program to one that uses it without using it. It's useful insofar as there simply are times where you really do want to create an async method that doesn't expose any way of observing the result, such as in the situation you mentioned. Could they have designed the feature in such a way that users could accomplish that another way, yes, you showed one possible way, the C# language designers choose another.
Exceptions from an unawaited async Task method will be unobserved, firing the TaskScheduler.UnobservedTaskException event. For example:
static void Main()
{
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs args) => { Console.WriteLine(args.Exception.InnerException.Message + " unobserved"); };
try
{
ThrowExceptionInAsyncTask();
Console.WriteLine("ThrowExceptionInAsyncTask not caught");
}
catch (Exception)
{
Console.WriteLine("ThrowExceptionInAsyncTask caught");
}
GC.Collect();
try
{
ThrowExceptionInAsyncVoid();
Console.WriteLine("ThrowExceptionInAsyncVoid not caught");
}
catch (Exception)
{
Console.WriteLine("ThrowExceptionInAsyncVoid caught");
}
GC.Collect();
}
static async Task ThrowExceptionInAsyncTask()
{
throw new InvalidOperationException("ThrowExceptionInAsyncTask");
}
static async void ThrowExceptionInAsyncVoid()
{
throw new InvalidOperationException("ThrowExceptionInAsyncVoid");
}
Produces:
ThrowExceptionInAsyncTask not caught
ThrowExceptionInAsyncVoid not caught
ThrowExceptionInAsyncTask unobserved
It is used in two cases as in my own knowledge:
In event handlers. Because event handlers cannot have a return type.
In a method intended to have no return type, and not awaited.
What you did here is a good workaround, but think of it as flexibility in language to make life easier for developers and simplify language syntax. Same example like yours is: Why System.Linq exists in C#?! Why we can use syntax like myIntArray.Max() while we can iterate through the array and find the maximum value!
That doesn't mean there is no other reason, but I'm sharing my thoughts and I hope that helps.
I have implemented the MVP (MVC) pattern in c# winforms.
My View and Presenter are as follows (without all the MVP glue):
public interface IExampleView
{
event EventHandler<EventArgs> SaveClicked;
string Message {get; set; }
}
public partial class ExampleView : Form
{
public event EventHandler<EventArgs> SaveClicked;
string Message {
get { return txtMessage.Text; }
set { txtMessage.Text = value; }
}
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null) SaveClicked.Invoke(sender, e);
}
}
public class ExamplePresenter
{
public void OnLoad()
{
View.SaveClicked += View_SaveClicked;
}
private async void View_SaveClicked(object sender, EventArgs e)
{
await Task.Run(() =>
{
// Do save
});
View.Message = "Saved!"
}
I am using MSTest for unit testing, along with NSubstitute for mocking. I want to simulate a button click in the view to test the controller's View_SaveClicked code as have the following:
[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
// Arrange
// Act
View.SaveClicked += Raise.EventWith(new object(), new EventArgs());
// Assert
Assert.AreEqual("Saved!", View.Message);
}
I am able to raise the View.SaveClicked successfully using NSubstitute's Raise.EventWith. However, the problem is that code immediately proceeds to the Assert before the Presenter has had time to save the message and the Assert fails.
I understand why this is happening and have managed to get around it by adding a Thread.Sleep(500) before the Assert, but this is less than ideal. I could also update my view to call a presenter.Save() method instead, but I would like the View to be Presenter agnostic as much as possible.
So would like to know I can improve the unit test to either wait for the async View_SaveClicked to finish or change the View/Presenter code to allow them to be unit tested easier in this situation.
Any ideas?
Since you are just concerned about unit testing, then you can use a custom SynchronizationContext, which allows you to detect the completion of async void methods.
You can use my AsyncContext type for this:
[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
// Arrange
AsyncContext.Run(() =>
{
// Act
View.SaveClicked += Raise.EventWith(new object(), new EventArgs());
});
// Assert
Assert.AreEqual("Saved!", View.Message);
}
However, it's best to avoid async void in your own code (as I describe in an MSDN article on async best practices). I have a blog post specifically about a few approaches on "async event handlers".
One approach is to replace all EventHandler<T> events with plain delegates, and call it via await:
public Func<Object, EventArgs, Task> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null) await SaveClicked(sender, e);
}
This is less pretty if you want a real event, though:
public delegate Task AsyncEventHandler<T>(object sender, T e);
public event AsyncEventHandler<EventArgs> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null)
await Task.WhenAll(
SaveClicked.GetInvocationList().Cast<AsyncEventHandler<T>>
.Select(x => x(sender, e)));
}
With this approach, any synchronous event handlers would need to return Task.CompletedTask at the end of the handler.
Another approach is to extend the EventArgs with a "deferral". This is also not pretty, but is more idiomatic for asynchronous event handlers.
There must be a some type work being done of the running task, and you need to use something to return a value from the task.
Seems like the Thread.Sleep helps mitigate that, though, might help to add some logic, and get a value from the task.
From: https://msdn.microsoft.com/en-us/library/mt674882.aspx