Thanks to visit codestin.com
Credit goes to github.com

Skip to content

AddValidatorsFromAssemblyContaining would register Validators twice #2182

@peterwurzinger

Description

@peterwurzinger

FluentValidation version

11.8.1

ASP.NET version

(ASP).NET Core 8

Summary

I honestly don't know if it is an issue, but it manifested as one to me when it occured.

2 (but actually any amount of) calls to AddValidatorsFromAssemblyContaining(<assembly>) would register every validator in the matching assembly 2 times. Clients, that retrieve something like IEnumerable<IValidator<TTarget>> via DI would get 2 instances of the same validator, potentially leading to executing it twice. Or thrice, or ... you get the point.

What sounds as an exotic use case in the first place, isn't too exotic for applications that make use of Vertical Slice Architecture. There the following structures are quite common:

  • Feature A
    -- Validators of Feature A (registered via FeatureA.ServiceCollectionExtensions.AddFeatureAServices)
  • Feature B
    -- Validators of Feature B (registered via FeatureA.ServiceCollectionExtensions.AddFeatureBServices)
  • Feature C
    -- ...

It basically boils down to the registrations in

services.Add(
new ServiceDescriptor(
serviceType: scanResult.InterfaceType,
implementationType: scanResult.ValidatorType,
lifetime: lifetime));
//Register as self
services.Add(
new ServiceDescriptor(
serviceType: scanResult.ValidatorType,
implementationType: scanResult.ValidatorType,
lifetime: lifetime));

Here the descriptors are added via the Add method.
Changing those to TryAdd, that verifies if a corresponding service descriptor is already contained in the service collection, would resolve this issue.

But as said, I can't tell if this is an inteded behavior for a use case that I currently cannot see. In that case a parameter for example could provice both options:

AddValidatorsFromAssemblyContaining(..., allowDuplicates: false) {
    if (allowDuplicates)
        services.Add(...)
    else
        services.TryAdd(...)
}

Steps to Reproduce

public class ArbitraryTarget{}

public class ArbitraryValidator : AbstractValidator<ArbitraryTarget> { ... }

//Program.cs
services.AddValidatorsFromAssemblyContaining<Program>();
services.AddValidatorsFromAssemblyContaining<Program>();

//Somewhere.cs
var provider = services.BuildServiceProvider();
var validators = provider.GetServices<IValidator<ArbitraryTarget>>();
//Inspect validators.Count();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions