|
7 | 7 | using System.Reflection; |
8 | 8 | using System.Reflection.Metadata; |
9 | 9 | using System.Runtime.InteropServices; |
| 10 | +using Microsoft.AspNetCore.Http.Validation; |
| 11 | +using Microsoft.Extensions.DependencyInjection; |
| 12 | +using Microsoft.Extensions.Options; |
10 | 13 |
|
11 | 14 | [assembly: MetadataUpdateHandler(typeof(Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions))] |
12 | 15 |
|
@@ -112,6 +115,13 @@ private void OnFieldChanged(object? sender, FieldChangedEventArgs eventArgs) |
112 | 115 | private void OnValidationRequested(object? sender, ValidationRequestedEventArgs e) |
113 | 116 | { |
114 | 117 | var validationContext = new ValidationContext(_editContext.Model, _serviceProvider, items: null); |
| 118 | + |
| 119 | + if (TryValidateTypeInfo(validationContext)) |
| 120 | + { |
| 121 | + _editContext.NotifyValidationStateChanged(); |
| 122 | + return; |
| 123 | + } |
| 124 | + |
115 | 125 | var validationResults = new List<ValidationResult>(); |
116 | 126 | Validator.TryValidateObject(_editContext.Model, validationContext, validationResults, true); |
117 | 127 |
|
@@ -140,6 +150,52 @@ private void OnValidationRequested(object? sender, ValidationRequestedEventArgs |
140 | 150 | _editContext.NotifyValidationStateChanged(); |
141 | 151 | } |
142 | 152 |
|
| 153 | +#pragma warning disable ASP0029 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. |
| 154 | + private bool TryValidateTypeInfo(ValidationContext validationContext) |
| 155 | + { |
| 156 | + var options = _serviceProvider?.GetService<IOptions<ValidationOptions>>()?.Value; |
| 157 | + |
| 158 | + if (options == null || !options.TryGetValidatableTypeInfo(_editContext.Model.GetType(), out var typeInfo)) |
| 159 | + { |
| 160 | + return false; |
| 161 | + } |
| 162 | + |
| 163 | + var validateContext = new ValidateContext |
| 164 | + { |
| 165 | + ValidationOptions = options, |
| 166 | + ValidationContext = validationContext, |
| 167 | + }; |
| 168 | + |
| 169 | + var validationTask = typeInfo.ValidateAsync(_editContext.Model, validateContext, CancellationToken.None); |
| 170 | + |
| 171 | + if (!validationTask.IsCompleted) |
| 172 | + { |
| 173 | + throw new InvalidOperationException("Async validation is not supported"); |
| 174 | + } |
| 175 | + |
| 176 | + var validationErrors = validateContext.ValidationErrors; |
| 177 | + |
| 178 | + // Transfer results to the ValidationMessageStore |
| 179 | + _messages.Clear(); |
| 180 | + |
| 181 | + if (validationErrors is not null && validationErrors.Count > 0) |
| 182 | + { |
| 183 | + foreach (var (key, value) in validationErrors) |
| 184 | + { |
| 185 | + var keySegments = key.Split('.'); |
| 186 | + var container = keySegments.Length > 1 |
| 187 | + ? GetPropertyByPath(_editContext.Model, keySegments[..^1]) |
| 188 | + : _editContext.Model; |
| 189 | + var fieldName = keySegments[^1]; |
| 190 | + |
| 191 | + _messages.Add(new FieldIdentifier(container, fieldName), value); |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + return true; |
| 196 | + } |
| 197 | +#pragma warning restore ASP0029 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. |
| 198 | + |
143 | 199 | public void Dispose() |
144 | 200 | { |
145 | 201 | _messages.Clear(); |
@@ -174,5 +230,20 @@ internal void ClearCache() |
174 | 230 | { |
175 | 231 | _propertyInfoCache.Clear(); |
176 | 232 | } |
| 233 | + |
| 234 | + // TODO(OR): Replace this with more robust implementation. |
| 235 | + private static object GetPropertyByPath(object obj, string[] path) |
| 236 | + { |
| 237 | + var currentObject = obj; |
| 238 | + |
| 239 | + foreach (string propertyName in path) |
| 240 | + { |
| 241 | + Type currentType = currentObject!.GetType(); |
| 242 | + PropertyInfo propertyInfo = currentType.GetProperty(propertyName)!; |
| 243 | + currentObject = propertyInfo.GetValue(currentObject); |
| 244 | + } |
| 245 | + |
| 246 | + return currentObject!; |
| 247 | + } |
177 | 248 | } |
178 | 249 | } |
0 commit comments