fix(ValidateForm): wait all field validate when submit form#7454
fix(ValidateForm): wait all field validate when submit form#7454
Conversation
Reviewer's guide (collapsed on small PRs)Reviewer's GuideAdjusts ValidateForm’s validation flow so that form submission waits for all field validations to complete, centralizing success/failure signaling in ValidateObject and simplifying the Validate() API. Sequence diagram for updated ValidateForm submission and validation flowsequenceDiagram
actor User
participant EditForm
participant ValidateForm
participant ValidatorCollection as Validators
participant Validator as IValidateComponent
participant Tcs as TaskCompletionSource_bool_
User->>EditForm: Submit()
EditForm->>ValidateForm: OnValidSubmitForm(EditContext)
ValidateForm->>ValidateForm: ValidateObject(ValidationContext, results)
ValidateForm->>Tcs: _tcs = new TaskCompletionSource_bool_()
ValidateForm->>Validators: Enumerate validators
loop For each field validator
ValidateForm->>Validator: ValidateAsync(validator, context)
alt Built_in_validation
Validator-->>ValidateForm: property value
ValidateForm->>ValidateForm: ValidateDataAnnotations(...)
else Custom_validation
Validator-->>ValidateForm: property value
ValidateForm->>Validator: ValidatePropertyAsync(propertyValue, context, messages)
end
ValidateForm->>Validator: ToggleMessage(messages)
end
ValidateForm->>ValidateForm: Aggregate results
ValidateForm->>Tcs: _tcs.TrySetResult(results.Count == 0)
EditForm-->>ValidateForm: Await _tcs.Task (all fields done)
alt All fields valid
ValidateForm-->>EditForm: Validation success
EditForm-->>User: Invoke success handler
else Some fields invalid
ValidateForm-->>EditForm: Validation failed
EditForm-->>User: Show validation errors
end
Updated class diagram for ValidateForm validation state handlingclassDiagram
class ValidateForm {
- TaskCompletionSource_bool_ _tcs
- List_ButtonBase_ AsyncSubmitButtons
+ Task ValidateObject(ValidationContext context, List_ValidationResult_ results)
- Task ValidateAsync(IValidateComponent validator, ValidationContext context)
+ bool Validate()
}
class IValidateComponent {
<<interface>>
+ Task ValidatePropertyAsync(object propertyValue, ValidationContext context, List_ValidationResult_ messages)
+ Task ToggleMessage(List_ValidationResult_ messages)
}
class ValidationContext
class ValidationResult
class ButtonBase
ValidateForm --> IValidateComponent : uses
ValidateForm --> ValidationContext : uses
ValidateForm --> ValidationResult : uses
ValidateForm --> ButtonBase : manages async submit buttons
IValidateComponent --> ValidationContext : uses
IValidateComponent --> ValidationResult : produces messages
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Re-evaluate using a shared
_tcsfield for coordination: ifValidateObjectcan be called concurrently, reassigning_tcsper call can lead to race conditions or tasks that are never completed for earlier callers; consider scoping theTaskCompletionSourceto the call or otherwise synchronizing access. - Now that
_tcsis only completed at the end ofValidateObject, ensure that all code paths (including early returns and exception paths) either set a result or fault theTaskCompletionSource, so no awaiting code can hang indefinitely.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Re-evaluate using a shared `_tcs` field for coordination: if `ValidateObject` can be called concurrently, reassigning `_tcs` per call can lead to race conditions or tasks that are never completed for earlier callers; consider scoping the `TaskCompletionSource` to the call or otherwise synchronizing access.
- Now that `_tcs` is only completed at the end of `ValidateObject`, ensure that all code paths (including early returns and exception paths) either set a result or fault the `TaskCompletionSource`, so no awaiting code can hang indefinitely.
## Individual Comments
### Comment 1
<location> `src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs:273-276` </location>
<code_context>
/// <param name="results"></param>
internal async Task ValidateObject(ValidationContext context, List<ValidationResult> results)
{
+ _tcs = new TaskCompletionSource<bool>();
_validateResults.Clear();
</code_context>
<issue_to_address>
**issue (bug_risk):** TaskCompletionSource may remain uncompleted if validation throws before reaching TrySetResult
Since `_tcs` is set at the start of `ValidateObject` and only completed at the end, any exception in the method (e.g. from `validator.ToggleMessage` or inside the loop) will leave it incomplete and hang awaiting callers. Please ensure `_tcs` is always completed (e.g. via a `try`/`finally` that calls `TrySetResult(false)` or `TrySetCanceled` on error).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7454 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 749 749
Lines 32919 32908 -11
Branches 4574 4572 -2
=========================================
- Hits 32919 32908 -11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This pull request fixes an issue where form validation wasn't properly waiting for all field validations to complete before determining the form's validity. The fix refactors the TaskCompletionSource logic to ensure synchronization across all field validations.
Key changes:
- Moved TaskCompletionSource initialization to the beginning of
ValidateObjectmethod to track the entire validation process - Removed the
_invalidfield and related logic that was causing incorrect validation state tracking - Simplified the
Validate()method by removing unnecessary_invalidflag manipulation
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs | Refactored validation logic to properly wait for all field validations by moving TaskCompletionSource creation to the start of ValidateObject and setting the result only after all fields are validated; removed the problematic _invalid field |
| src/BootstrapBlazor/BootstrapBlazor.csproj | Incremented version from 10.2.1-beta02 to 10.2.1-beta03 for this bug fix release |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// <param name="results"></param> | ||
| internal async Task ValidateObject(ValidationContext context, List<ValidationResult> results) | ||
| { | ||
| _tcs = new TaskCompletionSource<bool>(); |
There was a problem hiding this comment.
There is a potential concurrency issue with the TaskCompletionSource field. If ValidateObject is called multiple times before the previous validation completes (e.g., rapid form submissions), the _tcs field could be overwritten, causing the previous await in OnValidSubmitForm to wait on the wrong task. Consider using a local variable instead of a field, or implementing proper synchronization to prevent concurrent validations.
Link issues
fixes #7440
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Ensure form submission waits for completion of all field validations before determining overall validity.
Bug Fixes:
Enhancements: