feat: rename validation builder for parity
Rename extensions functions and `IValidatorBuilder` for parity with dispatcher options and builder.
This commit is contained in:
parent
4d33d5ab4c
commit
36f1a9eb1b
7 changed files with 52 additions and 51 deletions
|
|
@ -33,6 +33,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- **request.dispatcher:** Hide pipeline internals in stack frames
|
- **request.dispatcher:** Hide pipeline internals in stack frames
|
||||||
|
- **request.validation:** Rename `IValidatorBuilder` to `IRequestValidatorBuilder` incl. extensions methods
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_single_validator()
|
public async Task I_can_resolve_single_validator()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<OrderedFailuresValidator>(ServiceLifetime.Transient);
|
builder.Add<OrderedFailuresValidator>(ServiceLifetime.Transient);
|
||||||
});
|
});
|
||||||
|
|
@ -27,7 +27,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_multiple_validators()
|
public async Task I_can_resolve_multiple_validators()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<PersonValidator>(ServiceLifetime.Transient);
|
builder.Add<PersonValidator>(ServiceLifetime.Transient);
|
||||||
builder.Add<AnotherPersonValidator>(ServiceLifetime.Transient);
|
builder.Add<AnotherPersonValidator>(ServiceLifetime.Transient);
|
||||||
|
|
@ -46,7 +46,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_open_generic_validator()
|
public async Task I_can_resolve_open_generic_validator()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add(typeof(GenericValidator<>), ServiceLifetime.Transient);
|
builder.Add(typeof(GenericValidator<>), ServiceLifetime.Transient);
|
||||||
});
|
});
|
||||||
|
|
@ -63,7 +63,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_multi_interface_validator()
|
public async Task I_can_resolve_multi_interface_validator()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<MultiInterfaceValidator>(ServiceLifetime.Transient);
|
builder.Add<MultiInterfaceValidator>(ServiceLifetime.Transient);
|
||||||
});
|
});
|
||||||
|
|
@ -84,7 +84,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_generic_validator_with_constraints()
|
public async Task I_can_resolve_generic_validator_with_constraints()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add(typeof(ConstrainedValidator<>), ServiceLifetime.Transient);
|
builder.Add(typeof(ConstrainedValidator<>), ServiceLifetime.Transient);
|
||||||
});
|
});
|
||||||
|
|
@ -104,7 +104,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_generic_wrapper_validator_with_constraints()
|
public async Task I_can_resolve_generic_wrapper_validator_with_constraints()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add(typeof(ConstrainedWrapperValidator<>), ServiceLifetime.Transient);
|
builder.Add(typeof(ConstrainedWrapperValidator<>), ServiceLifetime.Transient);
|
||||||
});
|
});
|
||||||
|
|
@ -124,7 +124,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_resolve_aggregate_validator_directly()
|
public async Task I_can_resolve_aggregate_validator_directly()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<PersonValidator>(ServiceLifetime.Transient);
|
builder.Add<PersonValidator>(ServiceLifetime.Transient);
|
||||||
builder.Add<AnotherPersonValidator>(ServiceLifetime.Transient);
|
builder.Add<AnotherPersonValidator>(ServiceLifetime.Transient);
|
||||||
|
|
@ -142,7 +142,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_select_validators_from_base_classes_and_interfaces()
|
public async Task I_can_select_validators_from_base_classes_and_interfaces()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<EntityValidator>(ServiceLifetime.Transient);
|
builder.Add<EntityValidator>(ServiceLifetime.Transient);
|
||||||
builder.Add<AnimalValidator>(ServiceLifetime.Transient);
|
builder.Add<AnimalValidator>(ServiceLifetime.Transient);
|
||||||
|
|
@ -161,7 +161,7 @@ internal sealed class DependencyInjectionTests
|
||||||
public async Task I_can_select_validators_polymorphically_based_on_the_instance_type()
|
public async Task I_can_select_validators_polymorphically_based_on_the_instance_type()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddValidation(builder =>
|
services.AddRequestValidation(builder =>
|
||||||
{
|
{
|
||||||
builder.Add<AnimalValidator>(ServiceLifetime.Transient);
|
builder.Add<AnimalValidator>(ServiceLifetime.Transient);
|
||||||
builder.Add<DogValidator>(ServiceLifetime.Transient);
|
builder.Add<DogValidator>(ServiceLifetime.Transient);
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ internal sealed class DispatchingValidator : IValidator
|
||||||
{
|
{
|
||||||
public override Validation Validate(ValidationContext context, IServiceProvider serviceProvider)
|
public override Validation Validate(ValidationContext context, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
var options = serviceProvider.GetRequiredService<IOptions<ValidationOptions>>().Value;
|
var options = serviceProvider.GetRequiredService<IOptions<RequestValidatorOptions>>().Value;
|
||||||
|
|
||||||
var validators = options.GetValidators<T>(serviceProvider);
|
var validators = options.GetValidators<T>(serviceProvider);
|
||||||
|
|
||||||
|
|
|
||||||
17
src/request.validation/IRequestValidatorBuilder.cs
Normal file
17
src/request.validation/IRequestValidatorBuilder.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) The Geekeey Authors
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Geekeey.Request.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a builder for configuring validator registrations.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRequestValidatorBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the service collection where the validators are registered.
|
||||||
|
/// </summary>
|
||||||
|
IServiceCollection Services { get; }
|
||||||
|
}
|
||||||
167
src/request.validation/RequestValidatorBuilderExtensions.cs
Normal file
167
src/request.validation/RequestValidatorBuilderExtensions.cs
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright (c) The Geekeey Authors
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
|
using static Geekeey.Request.Validation.RequestValidatorOptions;
|
||||||
|
|
||||||
|
namespace Geekeey.Request.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for configuring <see cref="IRequestValidatorBuilder"/>
|
||||||
|
/// with additional capabilities such as searching and registering validators in assemblies or adding types directly.
|
||||||
|
/// </summary>
|
||||||
|
public static class RequestValidatorBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for validator types within the specified assembly and adds them to the validator
|
||||||
|
/// configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="assembly">The assembly to search for validator types.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder SearchInAssembly(this IRequestValidatorBuilder builder, Assembly assembly)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
var exports = assembly.GetTypes()
|
||||||
|
.Where(type => type is { IsClass: true, IsAbstract: false })
|
||||||
|
.Where(IsValidatorImplementationType);
|
||||||
|
|
||||||
|
builder.Add(exports);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for validator types within the specified assembly and adds them to the validator
|
||||||
|
/// configuration with the given service lifetime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="assembly">The assembly to search for validator types.</param>
|
||||||
|
/// <param name="lifetime">The lifetime with which the validators are registered in the dependency injection container.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder SearchInAssembly(this IRequestValidatorBuilder builder, Assembly assembly, ServiceLifetime lifetime)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
var exports = assembly.GetTypes()
|
||||||
|
.Where(type => type is { IsClass: true, IsAbstract: false })
|
||||||
|
.Where(IsValidatorImplementationType);
|
||||||
|
|
||||||
|
builder.Add(exports, lifetime);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified type to the validator configuration for inspection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="type">The type to be added to the validator configuration.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, Type type)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
builder.Services.AddOptions<RequestValidatorOptions>()
|
||||||
|
.Configure(options => options.Inspect([type]));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified type to the validator configuration.
|
||||||
|
/// This also adds the type to the service collection with the specified lifetime,
|
||||||
|
/// allowing it to be resolved as a dependency.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> used to configure the validators.</param>
|
||||||
|
/// <param name="type">The type to be added to the validator configuration.</param>
|
||||||
|
/// <param name="lifetime">The lifetime scope of the type in the service container.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, Type type, ServiceLifetime lifetime)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
builder.Services.AddOptions<RequestValidatorOptions>()
|
||||||
|
.Configure(options => options.Inspect([type]));
|
||||||
|
|
||||||
|
builder.Services.Add(new ServiceDescriptor(type, type, lifetime));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified collection of types to the validator configuration for inspection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="types">The collection of types to be added to the validator configuration.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, IEnumerable<Type> types)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
var typeList = types.ToList();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<RequestValidatorOptions>()
|
||||||
|
.Configure(options => options.Inspect(typeList));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified collection of types to the validator configuration for inspection.
|
||||||
|
/// This also adds the specified collection of types to the service collection with the specified lifetime,
|
||||||
|
/// allowing it to be resolved as a dependency.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="types">The collection of types to be added to the validator configuration.</param>
|
||||||
|
/// <param name="lifetime">The lifetime scope of the types in the service container.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, IEnumerable<Type> types, ServiceLifetime lifetime)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|
||||||
|
var typeList = types.ToList();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<RequestValidatorOptions>()
|
||||||
|
.Configure(options => options.Inspect(typeList));
|
||||||
|
|
||||||
|
builder.Services.Add(typeList.Select(export => new ServiceDescriptor(export, export, lifetime)));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified validator type to the validator configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValidator">The type of the validator to add.</typeparam>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add<TValidator>(this IRequestValidatorBuilder builder)
|
||||||
|
where TValidator : class, IValidator
|
||||||
|
{
|
||||||
|
return builder.Add(typeof(TValidator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified validator type to the validator configuration with the specified lifetime.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValidator">The type of the validator to add.</typeparam>
|
||||||
|
/// <param name="builder">The <see cref="IRequestValidatorBuilder"/> to configure.</param>
|
||||||
|
/// <param name="lifetime">The lifetime scope of the validator in the service container.</param>
|
||||||
|
/// <returns>The <see cref="IRequestValidatorBuilder"/> instance for further configuration.</returns>
|
||||||
|
public static IRequestValidatorBuilder Add<TValidator>(this IRequestValidatorBuilder builder, ServiceLifetime lifetime)
|
||||||
|
where TValidator : class, IValidator
|
||||||
|
{
|
||||||
|
return builder.Add(typeof(TValidator), lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidatorImplementationType(Type type)
|
||||||
|
{
|
||||||
|
return type.GetInterfaces().Any(IsValidatorType);
|
||||||
|
}
|
||||||
|
}
|
||||||
145
src/request.validation/RequestValidatorOptions.cs
Normal file
145
src/request.validation/RequestValidatorOptions.cs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright (c) The Geekeey Authors
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Geekeey.Request.Validation;
|
||||||
|
|
||||||
|
internal sealed class RequestValidatorOptions
|
||||||
|
{
|
||||||
|
private readonly List<Type> _search = [];
|
||||||
|
private readonly Lazy<TypeIndex> _validatorsTypeIndex;
|
||||||
|
|
||||||
|
public RequestValidatorOptions()
|
||||||
|
{
|
||||||
|
_validatorsTypeIndex = new Lazy<TypeIndex>(() => new ValidatorTypeIndex(_search.Distinct()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Inspect(IEnumerable<Type> assembly)
|
||||||
|
{
|
||||||
|
if (_validatorsTypeIndex.IsValueCreated)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The type index has already been created. Cannot inspect new assemblies.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_search.AddRange(assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IValidator<T>> GetValidators<T>(IServiceProvider services)
|
||||||
|
{
|
||||||
|
return _validatorsTypeIndex.Value.Resolve<IValidator<T>>(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class TypeIndex
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<Type, Func<IServiceProvider, IEnumerable>> _cache = new();
|
||||||
|
|
||||||
|
protected readonly Dictionary<Type, List<Type>> _closedTypeInfo = [];
|
||||||
|
protected readonly List<Type> _openTypeInfo = [];
|
||||||
|
|
||||||
|
protected TypeIndex(IEnumerable<Type> collection, Func<Type, bool> predicate)
|
||||||
|
{
|
||||||
|
foreach (var type in collection)
|
||||||
|
{
|
||||||
|
if (type.IsGenericTypeDefinition)
|
||||||
|
{
|
||||||
|
if (type.GetInterfaces().Any(predicate))
|
||||||
|
{
|
||||||
|
_openTypeInfo.Add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var @interface in type.GetInterfaces().Where(predicate))
|
||||||
|
{
|
||||||
|
(_closedTypeInfo.TryGetValue(@interface, out var list) ? list : _closedTypeInfo[@interface] = []).Add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> Resolve<T>(IServiceProvider services)
|
||||||
|
{
|
||||||
|
return (IEnumerable<T>)_cache.GetOrAdd(typeof(T), CreateResolverFactory<T>)(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract IReadOnlyList<Type> IsAssignableTo(Type type);
|
||||||
|
|
||||||
|
private Func<IServiceProvider, IEnumerable<T>> CreateResolverFactory<T>(Type @interface)
|
||||||
|
{
|
||||||
|
var list = IsAssignableTo(@interface);
|
||||||
|
return ResolverFactory;
|
||||||
|
|
||||||
|
IEnumerable<T> ResolverFactory(IServiceProvider services)
|
||||||
|
{
|
||||||
|
foreach (var type in list)
|
||||||
|
{
|
||||||
|
yield return (T)ActivatorUtilities.GetServiceOrCreateInstance(services, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsValidatorType(Type type)
|
||||||
|
{
|
||||||
|
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IValidator<>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ValidatorTypeIndex(IEnumerable<Type> collection)
|
||||||
|
: TypeIndex(collection, IsValidatorType)
|
||||||
|
{
|
||||||
|
protected override IReadOnlyList<Type> IsAssignableTo(Type @interface)
|
||||||
|
{
|
||||||
|
var result = new List<Type>();
|
||||||
|
|
||||||
|
foreach (var kvp in _closedTypeInfo)
|
||||||
|
{
|
||||||
|
if (@interface.IsAssignableFrom(kvp.Key))
|
||||||
|
{
|
||||||
|
result.AddRange(kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatedType = @interface.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
foreach (var type in _openTypeInfo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// open type case one: Validator<T> : IValidator<T>
|
||||||
|
// We try to close it with the validated type.
|
||||||
|
var impl = type.MakeGenericType(validatedType);
|
||||||
|
if (impl.IsAssignableTo(@interface))
|
||||||
|
{
|
||||||
|
result.Add(impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// open type case two: Validator<T> : IValidator<Wrapper<T>>
|
||||||
|
// If the validated type is generic, we try to close the validator with the validated type's generic arguments.
|
||||||
|
if (validatedType.IsGenericType)
|
||||||
|
{
|
||||||
|
var impl = type.MakeGenericType(validatedType.GetGenericArguments());
|
||||||
|
if (impl.IsAssignableTo(@interface))
|
||||||
|
{
|
||||||
|
result.Add(impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,15 +14,15 @@ public static class ServiceCollectionExtensions
|
||||||
/// Adds validator services to the specified <see cref="IServiceCollection"/>.
|
/// Adds validator services to the specified <see cref="IServiceCollection"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">The service collection to which the validator services will be added.</param>
|
/// <param name="services">The service collection to which the validator services will be added.</param>
|
||||||
/// <returns>An instance of <see cref="IValidatorBuilder"/> to configure the validator registrations.</returns>
|
/// <returns>An instance of <see cref="IRequestValidatorBuilder"/> to configure the validator registrations.</returns>
|
||||||
public static IValidatorBuilder AddValidation(this IServiceCollection services)
|
public static IRequestValidatorBuilder AddRequestValidation(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(services);
|
ArgumentNullException.ThrowIfNull(services);
|
||||||
|
|
||||||
services.AddOptions<ValidationOptions>();
|
services.AddOptions<RequestValidatorOptions>();
|
||||||
services.AddTransient<IValidator, DispatchingValidator>();
|
services.AddTransient<IValidator, DispatchingValidator>();
|
||||||
|
|
||||||
return new ValidatorBuilder(services);
|
return new RequestValidatorBuilder(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -32,17 +32,17 @@ public static class ServiceCollectionExtensions
|
||||||
/// <param name="services">The service collection to which the validator services will be added.</param>
|
/// <param name="services">The service collection to which the validator services will be added.</param>
|
||||||
/// <param name="configure">A delegate to configure the validator builder.</param>
|
/// <param name="configure">A delegate to configure the validator builder.</param>
|
||||||
/// <returns>The service collection with the validator services added.</returns>
|
/// <returns>The service collection with the validator services added.</returns>
|
||||||
public static IServiceCollection AddValidation(this IServiceCollection services, Action<IValidatorBuilder> configure)
|
public static IServiceCollection AddRequestValidation(this IServiceCollection services, Action<IRequestValidatorBuilder> configure)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(services);
|
ArgumentNullException.ThrowIfNull(services);
|
||||||
ArgumentNullException.ThrowIfNull(configure);
|
ArgumentNullException.ThrowIfNull(configure);
|
||||||
|
|
||||||
configure(services.AddValidation());
|
configure(services.AddRequestValidation());
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ValidatorBuilder(IServiceCollection services) : IValidatorBuilder
|
private sealed class RequestValidatorBuilder(IServiceCollection services) : IRequestValidatorBuilder
|
||||||
{
|
{
|
||||||
public IServiceCollection Services { get; } = services;
|
public IServiceCollection Services { get; } = services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue