Skip to content

Commit f1e1feb

Browse files
authored
feat(ValidateForm): unify async validation APIs across frameworks (#8441)
* refactor: 重构代码(先出门) * test: 更新单元测试 * test: 增加单元测试提高代码覆盖率 * chore: bump version 10.10.1 * revert: 撤销弃用方法 * refactor: 删除不需要的单元测试 * refactor: 移除同步验证方法
1 parent 95776d1 commit f1e1feb

9 files changed

Lines changed: 1165 additions & 193 deletions

File tree

‎src/BootstrapBlazor/Attributes/AsyncValidationAttribute.cs‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ protected AsyncValidationAttribute(Func<string> errorMessageAccessor) : base(err
6969
/// <param name="cancellationToken"></param>
7070
public async Task<ValidationResult?> GetValidationResultAsync(object? value, ValidationContext validationContext, CancellationToken cancellationToken = default)
7171
{
72-
ArgumentNullException.ThrowIfNull(validationContext);
73-
7472
var result = await IsValidAsync(value, validationContext, cancellationToken).ConfigureAwait(false);
7573
if (result != null && string.IsNullOrEmpty(result.ErrorMessage))
7674
{

‎src/BootstrapBlazor/BootstrapBlazor.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>10.10.1-beta03</Version>
4+
<Version>10.10.1</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

‎src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorDataAnnotationsValidator.cs‎

Lines changed: 12 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -68,175 +68,37 @@ private void RemoveEditContextDataAnnotationsValidation()
6868
CurrentEditContext.OnFieldChanged -= OnFieldChanged;
6969
}
7070

71-
#if NET11_0_OR_GREATER
7271
internal Task<bool> ValidateAsync(CancellationToken cancellationToken = default) => CurrentEditContext.ValidateAsync(cancellationToken);
7372

7473
private void OnValidationRequested(object? sender, ValidationRequestedEventArgs args)
7574
{
76-
args.AddAsyncValidator(cancellationToken => ValidateModelAsync(CurrentEditContext, _message, Provider, cancellationToken));
75+
args.AddAsyncValidator(ValidateModelAsync);
7776
}
7877

7978
private void OnFieldChanged(object? sender, FieldChangedEventArgs args)
8079
{
8180
var fieldIdentifier = args.FieldIdentifier;
8281
CurrentEditContext.RegisterAsyncFieldValidator(fieldIdentifier, cancellationToken => ValidateFieldAsync(CurrentEditContext, _message, fieldIdentifier, Provider, cancellationToken));
8382
}
84-
#else
85-
#if NET9_0_OR_GREATER
86-
private readonly Lock _fieldValidationLock = new();
87-
#else
88-
private readonly object _fieldValidationLock = new();
89-
#endif
90-
91-
private readonly SemaphoreSlim _validationLock = new(1, 1);
92-
private readonly Dictionary<FieldIdentifier, FieldValidationOperation> _fieldValidationOperations = [];
93-
private bool _suppressValidationRequested;
9483

95-
internal async Task<bool> ValidateAsync(CancellationToken cancellationToken = default)
84+
private async Task ValidateModelAsync(CancellationToken cancellationToken)
9685
{
97-
await _validationLock.WaitAsync(cancellationToken);
9886
try
9987
{
100-
CancelFieldValidations();
101-
_message.Clear();
102-
103-
bool synchronousValid;
104-
_suppressValidationRequested = true;
105-
try
106-
{
107-
// NET10 只有同步方法
108-
synchronousValid = CurrentEditContext.Validate();
109-
}
110-
finally
111-
{
112-
_suppressValidationRequested = false;
113-
}
114-
115-
// 通过 _suppressValidationRequested 控制 OnValidationRequested 事件中的 ValidateModelAsync 不被调用,避免重复验证
116-
var valid = await ValidateModelAsync(CurrentEditContext, _message, Provider, cancellationToken);
117-
return synchronousValid && valid && !CurrentEditContext.GetValidationMessages().Any();
88+
await ValidateModelAsync(CurrentEditContext, _message, Provider, cancellationToken);
11889
}
119-
catch (OperationCanceledException)
90+
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
12091
{
12192
throw;
12293
}
12394
catch (Exception exception)
12495
{
12596
Logger.LogError(exception, "An exception occurred while validating the form.");
126-
return false;
127-
}
128-
finally
129-
{
130-
_validationLock.Release();
131-
}
132-
}
133-
134-
private async void OnValidationRequested(object? sender, ValidationRequestedEventArgs args)
135-
{
136-
if (!_suppressValidationRequested)
137-
{
138-
try
139-
{
140-
await ValidateModelAsync(CurrentEditContext, _message, Provider, CancellationToken.None);
141-
}
142-
catch (Exception exception)
143-
{
144-
Logger.LogError(exception, "An exception occurred while validating the form.");
145-
}
146-
}
147-
}
148-
149-
private void OnFieldChanged(object? sender, FieldChangedEventArgs args)
150-
{
151-
var fieldIdentifier = args.FieldIdentifier;
152-
153-
FieldValidationOperation? previousOperation;
154-
FieldValidationOperation operation;
155-
lock (_fieldValidationLock)
156-
{
157-
_fieldValidationOperations.Remove(fieldIdentifier, out previousOperation);
158-
operation = new FieldValidationOperation();
159-
_fieldValidationOperations.Add(fieldIdentifier, operation);
160-
}
161-
previousOperation?.Cancel();
162-
_ = ValidateFieldAndCleanupAsync(fieldIdentifier, operation);
163-
}
164-
165-
private async Task ValidateFieldAndCleanupAsync(FieldIdentifier fieldIdentifier, FieldValidationOperation operation)
166-
{
167-
try
168-
{
169-
await ValidateFieldAsync(CurrentEditContext, _message, fieldIdentifier, Provider, operation.Token);
170-
}
171-
catch (OperationCanceledException) when (operation.IsCancellationRequested)
172-
{
173-
}
174-
finally
175-
{
176-
lock (_fieldValidationLock)
177-
{
178-
if (_fieldValidationOperations.TryGetValue(fieldIdentifier, out var currentOperation)
179-
&& ReferenceEquals(currentOperation, operation))
180-
{
181-
_fieldValidationOperations.Remove(fieldIdentifier);
182-
}
183-
}
184-
operation.Complete();
185-
}
186-
}
187-
188-
private void CancelFieldValidations()
189-
{
190-
FieldValidationOperation[] operations;
191-
lock (_fieldValidationLock)
192-
{
193-
operations = [.. _fieldValidationOperations.Values];
194-
_fieldValidationOperations.Clear();
195-
}
196-
foreach (var operation in operations)
197-
{
198-
operation.Cancel();
199-
}
200-
}
201-
202-
private sealed class FieldValidationOperation
203-
{
204-
private CancellationTokenSource? _tokenSource;
205-
206-
public CancellationToken Token { get; }
207-
208-
public bool IsCancellationRequested => Token.IsCancellationRequested;
209-
210-
public FieldValidationOperation()
211-
{
212-
_tokenSource = new();
213-
Token = _tokenSource.Token;
214-
}
215-
216-
public void Cancel()
217-
{
218-
var tokenSource = Interlocked.Exchange(ref _tokenSource, null);
219-
if (tokenSource != null)
220-
{
221-
try
222-
{
223-
tokenSource.Cancel();
224-
}
225-
finally
226-
{
227-
tokenSource.Dispose();
228-
}
229-
}
230-
}
231-
232-
public void Complete()
233-
{
234-
Interlocked.Exchange(ref _tokenSource, null)?.Dispose();
97+
throw;
23598
}
23699
}
237-
#endif
238100

239-
private async Task<bool> ValidateModelAsync(EditContext editContext, ValidationMessageStore messages, IServiceProvider provider, CancellationToken cancellationToken)
101+
private async Task ValidateModelAsync(EditContext editContext, ValidationMessageStore messages, IServiceProvider provider, CancellationToken cancellationToken)
240102
{
241103
cancellationToken.ThrowIfCancellationRequested();
242104
var validationContext = new ValidationContext(editContext.Model, provider, null);
@@ -265,7 +127,6 @@ private async Task<bool> ValidateModelAsync(EditContext editContext, ValidationM
265127
}
266128
}
267129
editContext.NotifyValidationStateChanged();
268-
return validationResults.Count == 0;
269130
}
270131

271132
private async Task ValidateFieldAsync(EditContext editContext, ValidationMessageStore messages, FieldIdentifier field, IServiceProvider provider, CancellationToken cancellationToken)
@@ -290,9 +151,13 @@ private async Task ValidateFieldAsync(EditContext editContext, ValidationMessage
290151
{
291152
return;
292153
}
154+
catch (Exception exception)
155+
{
156+
Logger.LogError(exception, "An exception occurred while validating the field.");
157+
throw;
158+
}
293159

294160
messages.Add(field, validationResults.Where(v => !string.IsNullOrEmpty(v.ErrorMessage)).Select(result => result.ErrorMessage!));
295-
296161
editContext.NotifyValidationStateChanged();
297162
}
298163

@@ -301,7 +166,7 @@ private void Dispose(bool disposing)
301166
if (disposing)
302167
{
303168
#if !NET11_0_OR_GREATER
304-
CancelFieldValidations();
169+
CurrentEditContext.CancelAsyncFieldValidations();
305170
#endif
306171
RemoveEditContextDataAnnotationsValidation();
307172
}

0 commit comments

Comments
 (0)