Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MudForm and others: rename async methods #8875

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

henon
Copy link
Collaborator

@henon henon commented May 4, 2024

Description

I originally wanted to rename MudForm's IsValid and IsTouched but then decided to leave them be for now becahse EditForm and FluentValidation both use IsValid and if we change MudForm to Valid it would actually increase inconsistency instead of decreasing it. I also decided to leave IsTouched alone. We might want to change it again after reworking form in v8 so it doesn't make much sense to touch it now.

But IFormComponent.IsForNull caught my eye, I believe ForIsNull is a better name so I renamed it.

Also overhauld MudForm's asynchronous validation and renamed several methods to bear the Async postfix.

Changes

  • IFormComponent: Renamed IsForNull to ForIsNull
  • DataGridRowValidator: Renamed Validate to ValidateAsync
  • TableRowValidator: Renamed Validate to ValidateAsync
  • MudForm: Replace Validate with ValidateAsync and await
  • MudForm: Replace ResetValidation with ResetValidationAsync and await

Type of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation (fix or improvement to the website or code docs)

Checklist

  • The PR is submitted to the correct branch (dev).
  • My code follows the code style of this project.
  • I've added relevant tests.

@henon henon added API change API that needs approval v7 New major MudBlazor version and removed PR: needs review labels May 4, 2024
@ScarletKuro
Copy link
Member

Should we rename Validate -> ValidateAsync?

Copy link

codecov bot commented May 4, 2024

Codecov Report

Attention: Patch coverage is 94.11765% with 1 lines in your changes are missing coverage. Please review.

Project coverage is 90.14%. Comparing base (28bc599) to head (6fe1023).
Report is 146 commits behind head on dev.

Files Patch % Lines
...Blazor/Components/DataGrid/DataGridRowValidator.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #8875      +/-   ##
==========================================
+ Coverage   89.82%   90.14%   +0.31%     
==========================================
  Files         412      421       +9     
  Lines       11878    12288     +410     
  Branches     2364     2428      +64     
==========================================
+ Hits        10670    11077     +407     
+ Misses        681      662      -19     
- Partials      527      549      +22     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@henon
Copy link
Collaborator Author

henon commented May 4, 2024

Maybe we should also make EvaluateForm and Update async and rename

@henon
Copy link
Collaborator Author

henon commented May 4, 2024

@ScarletKuro I made every method async and eliminated async Task discarding where possible except IForm.Update. Here is my reasoning embedded in a code comment in the method itself:

/// <summary>
/// Called by any input of the form to signal that its value changed. 
/// </summary>
/// <param name="formControl"></param>
void IForm.Update(IFormComponent formControl)
{
    // Note: We don't want to make IForm.Update async because it makes no sense for the inputs to wait
    // for form evaluation, especially when multiple input parameters change which results in several form 
    // updates which are debounced anyway via a timer.

    // Validation exceptions are observed via AndForget() which reports exceptions via MudGlobal.UnhandledExceptionHandler
    EvaluateFormAsync().AndForget();
}

@henon henon changed the title IFormComponent: rename IsForNull to ForIsNull MudForm and others: rename async methods May 4, 2024
@@ -15,7 +16,7 @@ public bool IsValid
{
get
{
Validate();
ValidateAsync().Wait();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this deadlock on WASM considering that .GetAwaiter().GetResult() does deadlock on WASM? And under the hood it's using Wait and this method in general causing thread pool starvation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, how would you synchronously await the validation result on WASM?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is not possible we'll have to keep this PR for v8

Copy link
Collaborator Author

@henon henon May 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the user of the validator should directly call ValidateAsync instead of using IsValid. ValidateAsync should return a Task<bool>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, how would you synchronously await the validation result on WASM?

That's the most annoying part :) you can't do it reliable on WASM without await like Wait, .GetAwaiter().GetResult(), .Result etc because this is all blocking calls to wait for the task to finish and this is not supported: dotnet/aspnetcore#18092 (comment)

@@ -10,7 +11,7 @@ public bool IsValid
{
get
{
Validate();
ValidateAsync().Wait();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in DataGridRowValidator

@ScarletKuro
Copy link
Member

@ScarletKuro I made every method async and eliminated async Task discarding where possible except IForm.Update. Here is my reasoning embedded in a code comment in the method itself:

I guess it makes sense.
Offtopic: we need to reconsider this whole validation for v8 to comply with our new rules of not overriding parameters etc, because I see validation is doing a lot of things that are not ok.
Also I find it weird that we provide IForm interface and allow people to override with their own implmentations since the properties for this public:

public IForm Validator { get; set; } = new DataGridRowValidator();

yet the interface has internal properties methods that are crucial.
Rere are some of complaints that mentions this moment: #6876, #7794

@ScarletKuro
Copy link
Member

ScarletKuro commented May 4, 2024

we need to reconsider this whole validation for v8

For example, it feels like the IsValid should either be a method like in datacontract OR it should have no logic, but like in fluentvalidation that some method call is calculating the IsValid and sets the state, current architecture makes IsValid to do some calculation when it's accessed and makes some async work to run which is not ok.

@henon henon marked this pull request as draft May 5, 2024 14:51
@henon
Copy link
Collaborator Author

henon commented May 5, 2024

This is a mega-project. Scheduling it for v8

@henon henon added v8 and removed v7 New major MudBlazor version labels May 5, 2024
@henon henon removed the request for review from Mr-Technician May 5, 2024 15:12
Copy link
Sponsor Contributor

@jperson2000 jperson2000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the renames looked consistent to me, and good catch changing void to Task for some async methods.

public bool IsTouched
{
get => _touched;
set {/* readonly parameter! */ }
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the setter be removed entirely, or is it needed for something like ParameterState?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it was coded like this to make sure everyone gets it that it is intentionally readonly. But I guess there are more modern ways to do it, I just don't know them.

}

private void EvaluateForm(bool debounce = true)
private Task EvaluateFormAsync(bool debounce = true)
{
_timer?.Dispose();
if (debounce && ValidationDelay > 0)
_timer = new Timer(OnTimerComplete, null, ValidationDelay, Timeout.Infinite);
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you need a timer which plays well with async code, consider the PeriodicTimer.

{
await Task.WhenAll(_formControls.Select(x => x.Validate()));
await Task.WhenAll(_formControls.Select(x => x.ValidateAsync()));
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two Task.WhenAll's could be combined if you add the tasks to a List<Task> then WhenAll on that list.


EvaluateForm(debounce: false);
await Task.WhenAll(_formControls.Select(x => x.ResetValidationAsync()));
await Task.WhenAll(ChildForms.Select(x => x.ResetValidationAsync()));
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two Task.WhenAll's could be combined into a List<Task> with a single WhenAll

ParentMudForm.EvaluateForm(); // Need this to refresh the form state
}
ParentMudForm?.ChildForms.Remove(this);
ParentMudForm?.EvaluateFormAsync().AndForget(); // Need this to refresh the form state. AndForget reports errors via MudGlobal.UnhandledExceptionHandler
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be changed to DisposeAsync and IAsyncDisposable? I believe the _timer also has a DisposeAsync()

@henon
Copy link
Collaborator Author

henon commented May 18, 2024

Thanks for the review @jperson2000, lots of good comments. This PR is on hold for now though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API change API that needs approval breaking change v8
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

None yet

3 participants