diff --git a/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationOperationFilter.cs b/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationOperationFilter.cs index 1f6a6a892..e705af4cc 100644 --- a/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationOperationFilter.cs +++ b/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationOperationFilter.cs @@ -20,29 +20,29 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi.Validation.Swashbuckle /// public class FluentValidationOperationFilter : IOperationFilter { + private readonly IValidatorFactory _validatorFactory; private readonly ILogger _logger; private readonly SwaggerGenOptions _swaggerGenOptions; - private readonly IValidatorFactory? _validatorFactory; private readonly IReadOnlyList _rules; /// /// Initializes a new instance of the class. /// /// Swagger generation options. - /// FluentValidation factory. + /// The validator factory. /// External FluentValidation rules. External rule overrides default rule with the same name. /// Logger factory. /// Schema generation options. public FluentValidationOperationFilter( IOptions swaggerGenOptions, - IValidatorFactory? validatorFactory = null, + IValidatorFactory validatorFactory, IEnumerable? rules = null, ILoggerFactory? loggerFactory = null, IOptions? options = null ) { - _swaggerGenOptions = swaggerGenOptions.Value; _validatorFactory = validatorFactory; + _swaggerGenOptions = swaggerGenOptions.Value; _logger = loggerFactory?.CreateLogger(typeof(FluentValidationRules)) ?? NullLogger.Instance; _rules = new DefaultFluentValidationRuleProvider(options).GetRules().ToArray().OverrideRules(rules); } @@ -66,12 +66,6 @@ private void ApplyInternal(OpenApiOperation operation, OperationFilterContext co if (operation.Parameters == null) return; - if (_validatorFactory == null) - { - _logger.LogWarning(0, "ValidatorFactory is not provided. Please register FluentValidation"); - return; - } - var schemaIdSelector = _swaggerGenOptions.SchemaGeneratorOptions.SchemaIdSelector ?? new SchemaGeneratorOptions().SchemaIdSelector; foreach (var operationParameter in operation.Parameters) diff --git a/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationRules.cs b/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationRules.cs index 4c3c39535..8691752ab 100644 --- a/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationRules.cs +++ b/src/AspNetCore/OpenApi/Validation/Swashbuckle/FluentValidationRules.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using FluentValidation; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -19,7 +18,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi.Validation.Swashbuckle /// public class FluentValidationRules : ISchemaFilter { - private readonly IServiceProvider _serviceProvider; + private readonly IValidatorFactory _validatorFactory; private readonly FluentValidationSwaggerGenOptions _options; private readonly ILogger _logger; private readonly IReadOnlyList _rules; @@ -27,18 +26,18 @@ public class FluentValidationRules : ISchemaFilter /// /// Initializes a new instance of the class. /// - /// The service provider. + /// The validator factory. /// External FluentValidation rules. External rule overrides default rule with the same name. /// for logging. Can be null. /// Schema generation options. public FluentValidationRules( - IServiceProvider serviceProvider, + IValidatorFactory validatorFactory, IEnumerable? rules = null, ILoggerFactory? loggerFactory = null, IOptions? options = null ) { - _serviceProvider = serviceProvider; + _validatorFactory = validatorFactory; _options = options?.Value ?? new FluentValidationSwaggerGenOptions(); _logger = loggerFactory?.CreateLogger(typeof(FluentValidationRules)) ?? NullLogger.Instance; _rules = new DefaultFluentValidationRuleProvider(options).GetRules().ToArray().OverrideRules(rules); @@ -47,12 +46,10 @@ public FluentValidationRules( /// public void Apply(OpenApiSchema schema, SchemaFilterContext context) { - using var scope = _serviceProvider.CreateScope(); - var validatorFactory = scope.ServiceProvider.GetRequiredService(); IValidator? validator = null; try { - validator = validatorFactory.GetValidator(context.Type); + validator = _validatorFactory.GetValidator(context.Type); } catch (Exception e) { diff --git a/src/Foundation/Conventions/FluentValidationConvention.cs b/src/Foundation/Conventions/FluentValidationConvention.cs index 7e004ba5a..3155f1bac 100644 --- a/src/Foundation/Conventions/FluentValidationConvention.cs +++ b/src/Foundation/Conventions/FluentValidationConvention.cs @@ -52,10 +52,14 @@ public void Register(IConventionContext context, IConfiguration configuration, I .GetCandidateAssemblies("FluentValidation"); foreach (var item in new AssemblyScanner(assemblies.SelectMany(z => z.DefinedTypes).Select(x => x.AsType()))) { - services.TryAddEnumerable(ServiceDescriptor.Describe(item.InterfaceType, item.ValidatorType, _options.ValidationLifetime)); + services.TryAddEnumerable(ServiceDescriptor.Describe(item.InterfaceType, item.ValidatorType, ServiceLifetime.Singleton)); } - services.TryAdd(ServiceDescriptor.Describe(typeof(IValidatorFactory), typeof(ValidatorFactory), _options.ValidationLifetime)); + if (services.FirstOrDefault(z => z.ServiceType == typeof(IValidatorFactory)) is { } s && s.Lifetime != ServiceLifetime.Singleton) + { + services.Remove(s); + } + services.TryAdd(ServiceDescriptor.Describe(typeof(IValidatorFactory), typeof(ValidatorFactory), ServiceLifetime.Singleton)); services.TryAddEnumerable(ServiceDescriptor.Describe(typeof(IPipelineBehavior<,>), typeof(ValidationPipelineBehavior<,>), _options.MediatorLifetime)); } } diff --git a/src/Foundation/FoundationOptions.cs b/src/Foundation/FoundationOptions.cs index f8e9d3981..14a72bc4c 100644 --- a/src/Foundation/FoundationOptions.cs +++ b/src/Foundation/FoundationOptions.cs @@ -29,9 +29,5 @@ public class FoundationOptions /// public ServiceLifetime MediatorLifetime { get; set; } = ServiceLifetime.Transient; - /// - /// The lifetime for validation services - /// - public ServiceLifetime ValidationLifetime { get; } = ServiceLifetime.Scoped; } } \ No newline at end of file diff --git a/src/HotChocolate/Conventions/GraphqlConvention.cs b/src/HotChocolate/Conventions/GraphqlConvention.cs index 1f7fb5cf0..1ccb64e02 100644 --- a/src/HotChocolate/Conventions/GraphqlConvention.cs +++ b/src/HotChocolate/Conventions/GraphqlConvention.cs @@ -55,7 +55,7 @@ public void Register(IConventionContext context, IConfiguration configuration, I ServiceDescriptor.Describe( typeof(IValidatorProvider), typeof(FairyBreadValidatorProvider), - services.FirstOrDefault(z => z.ServiceType == typeof(IValidatorFactory))?.Lifetime ?? _foundationOptions.ValidationLifetime + ServiceLifetime.Singleton ) ); services.TryAddSingleton(); diff --git a/test/AspNetCore.Tests/Validation/UnitTestBase.cs b/test/AspNetCore.Tests/Validation/UnitTestBase.cs index d02bcc096..4ed8599cb 100644 --- a/test/AspNetCore.Tests/Validation/UnitTestBase.cs +++ b/test/AspNetCore.Tests/Validation/UnitTestBase.cs @@ -1,4 +1,6 @@ +using FakeItEasy; using FluentValidation; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Rocket.Surgery.LaunchPad.AspNetCore.OpenApi.Validation; @@ -19,7 +21,8 @@ public SchemaGenerator SchemaGenerator(params IValidator[] validators) public SchemaGenerator SchemaGenerator( Action configureGenerator = null, - Action configureSerializer = null) + Action configureSerializer = null + ) { var generatorOptions = new SchemaGeneratorOptions(); configureGenerator?.Invoke(generatorOptions); @@ -33,7 +36,12 @@ public SchemaGenerator SchemaGenerator( private void ConfigureGenerator(SchemaGeneratorOptions options, params IValidator[] validators) { IValidatorFactory validatorFactory = new CustomValidatorFactory(validators); - options.SchemaFilters.Add(new FluentValidationRules(validatorFactory)); + + options.SchemaFilters.Add( + new FluentValidationRules( + validatorFactory + ) + ); } } @@ -54,7 +62,8 @@ public SchemaBuilder ConfigureFVSwaggerGenOptions(Action( Expression> propertyExpression, Action>? configureRule = null, - Action? schemaCheck = null) + Action? schemaCheck = null + ) { IRuleBuilderInitial ruleBuilder = Validator.RuleFor(propertyExpression); configureRule?.Invoke(ruleBuilder); @@ -72,12 +81,17 @@ public OpenApiSchema AddRule( public static class TestExtensions { - public static OpenApiSchema GenerateSchemaForValidator(this SchemaRepository schemaRepository, IValidator validator, FluentValidationSwaggerGenOptions? fluentValidationSwaggerGenOptions = null) + public static OpenApiSchema GenerateSchemaForValidator( + this SchemaRepository schemaRepository, + IValidator validator, + FluentValidationSwaggerGenOptions? fluentValidationSwaggerGenOptions = null + ) { OpenApiSchema schema = CreateSchemaGenerator( - new []{ validator }, - fluentValidationSwaggerGenOptions: fluentValidationSwaggerGenOptions) - .GenerateSchema(typeof(T), schemaRepository); + new[] { validator }, + fluentValidationSwaggerGenOptions: fluentValidationSwaggerGenOptions + ) + .GenerateSchema(typeof(T), schemaRepository); if (schema.Reference?.Id != null) schema = schemaRepository.Schemas[schema.Reference.Id]; @@ -87,23 +101,32 @@ public static OpenApiSchema GenerateSchemaForValidator(this SchemaRepository public static SchemaGenerator CreateSchemaGenerator( IValidator[] validators, - FluentValidationSwaggerGenOptions? fluentValidationSwaggerGenOptions = null) + FluentValidationSwaggerGenOptions? fluentValidationSwaggerGenOptions = null + ) { - return CreateSchemaGenerator(options => - { - IValidatorFactory validatorFactory = new CustomValidatorFactory(validators); - - options.SchemaFilters.Add(new FluentValidationRules( - validatorFactory: validatorFactory, - rules: null, - loggerFactory: null, - options: fluentValidationSwaggerGenOptions != null ? new OptionsWrapper(fluentValidationSwaggerGenOptions) : null)); - }); + return CreateSchemaGenerator( + options => + { + IValidatorFactory validatorFactory = new CustomValidatorFactory(validators); + + options.SchemaFilters.Add( + new FluentValidationRules( + validatorFactory, + rules: null, + loggerFactory: null, + options: fluentValidationSwaggerGenOptions != null + ? new OptionsWrapper(fluentValidationSwaggerGenOptions) + : null + ) + ); + } + ); } public static SchemaGenerator CreateSchemaGenerator( Action? configureGenerator = null, - Action? configureSerializer = null) + Action? configureSerializer = null + ) { var generatorOptions = new SchemaGeneratorOptions(); configureGenerator?.Invoke(generatorOptions);