Skip to content

Expose FluentValidation configuration through UseFluentValidation overload#2497

Merged
jeremydmiller merged 2 commits intoJasperFx:mainfrom
outofrange-consulting:feature/fluentvalidation-expose-config-pr
Apr 13, 2026
Merged

Expose FluentValidation configuration through UseFluentValidation overload#2497
jeremydmiller merged 2 commits intoJasperFx:mainfrom
outofrange-consulting:feature/fluentvalidation-expose-config-pr

Conversation

@outofrange-consulting
Copy link
Copy Markdown

@outofrange-consulting outofrange-consulting commented Apr 10, 2026

Summary

  • Adds a FluentValidationConfiguration class that exposes RegistrationBehavior, IncludeInternalTypes, and direct access to ValidatorOptions.Global (cascade modes, severity, language manager, property name resolvers, etc.)
  • Adds a new UseFluentValidation(Action<FluentValidationConfiguration>) overload — fully backward-compatible with the existing API
  • Adds IncludeInternalTypes option that uses FluentValidation's own AssemblyScanner to discover internal validators, since Lamar's ConnectImplementationsToTypesClosing only finds public types
  • Adds 7 tests covering options application, registration behavior, middleware wiring, internal type discovery, and defaults

Motivation

Two gaps in the current FluentValidation extension:

  1. No access to FluentValidation configuration — users cannot configure ValidatorOptions.Global (cascade modes, severity, language manager, etc.) through the Wolverine API. They must set these separately.

  2. Internal validators are not discovered — Lamar's assembly scanning uses Assembly.GetExportedTypes() which only returns public types. FluentValidation's own AssemblyScanner supports an includeInternalTypes parameter.

This change lets users configure everything in one place:

opts.UseFluentValidation(fv =>
{
    // Configure FluentValidation's global options
    fv.ValidatorOptions.DefaultRuleLevelCascadeMode = CascadeMode.Stop;
    fv.ValidatorOptions.Severity = Severity.Warning;

    // Discover internal validators too
    fv.IncludeInternalTypes = true;

    // Optionally control registration behavior
    fv.RegistrationBehavior = RegistrationBehavior.ExplicitRegistration;
});

Test plan

  • configure_validator_options_via_action_overload — verifies global options are applied
  • configure_registration_behavior_via_action_overload — verifies explicit registration through new overload
  • action_overload_still_applies_middleware — verifies middleware is still wired up
  • discover_internal_validators_when_include_internal_types_is_true — internal validator is registered as Singleton
  • do_not_discover_internal_validators_by_default — internal validators are NOT found without the flag
  • discover_internal_validator_with_dependencies_as_scoped — internal validator with ctor deps is Scoped
  • fluent_validation_configuration_defaults — verifies default values
  • All existing FluentValidation tests still pass (24/24 on net9.0 and net10.0)

Geoffrey MARC added 2 commits April 10, 2026 17:43
…rload

Add FluentValidationConfiguration class that exposes both RegistrationBehavior
and direct access to ValidatorOptions.Global (cascade modes, severity, language
manager, property name resolvers, etc.). Add new UseFluentValidation(Action<>)
overload that is fully backward-compatible with the existing API.
Use FluentValidation's own AssemblyScanner when IncludeInternalTypes is
enabled, since Lamar's ConnectImplementationsToTypesClosing only finds
public types. Preserves the existing lifetime logic (Scoped for validators
with constructor dependencies, Singleton otherwise).
@outofrange-consulting
Copy link
Copy Markdown
Author

Just want to say that I usually mark my FV validators as internal sealed because I don’t consider them as part of the public API, and FV does support it OOB. But maybe it’s a bit opiniated for Wolverine, not sure.
And since in a full Wolverine app I can (if I want) rely solely on Wolverine extensions, it felt natural to expose basic FV configuration also.

@jeremydmiller
Copy link
Copy Markdown
Member

Just want to say that I usually mark my FV validators as internal sealed because I don’t consider them as part of the public API, and FV does support it OOB. But maybe it’s a bit opiniated for Wolverine, not sure. And since in a full Wolverine app I can (if I want) rely solely on Wolverine extensions, it felt natural to expose basic FV configuration also.

So, I think the internal thing in this case just flat out does not add any value whatsoever and I've long thought that people waste too much time and energy on types that aren't exposed to the outside world. This would potentially break you if you w/ Wolverine if you had an internal type that was not a singleton as a validator.

But, I'd also say that if you have a Fluent Validation validator that requires access to IoC services, then it shouldn't be done w/ FV and you should use a Wolverine ValidateAsync() method for one off, special validation anyway instead of hiding it behind FV.

Did you forget to check in a markdown change? I see you have a code sample for the docs, but no doc changes in the PR.

@outofrange-consulting
Copy link
Copy Markdown
Author

Just want to say that I usually mark my FV validators as internal sealed because I don’t consider them as part of the public API, and FV does support it OOB. But maybe it’s a bit opiniated for Wolverine, not sure. And since in a full Wolverine app I can (if I want) rely solely on Wolverine extensions, it felt natural to expose basic FV configuration also.

So, I think the internal thing in this case just flat out does not add any value whatsoever and I've long thought that people waste too much time and energy on types that aren't exposed to the outside world. This would potentially break you if you w/ Wolverine if you had an internal type that was not a singleton as a validator.

But, I'd also say that if you have a Fluent Validation validator that requires access to IoC services, then it shouldn't be done w/ FV and you should use a Wolverine ValidateAsync() method for one off, special validation anyway instead of hiding it behind FV.

Did you forget to check in a markdown change? I see you have a code sample for the docs, but no doc changes in the PR.

No no I agree that validators shouldn’t require injection.
I’m OFF this week so can’t édit the PR, feel free to reject it for now, I’ll try to do better next week depending on what you think would be a good addition to the extension

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants