You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET依赖注入(结合Scrutor)中实现类似SimpleInjector的条件注册回退机制?

Great question! When moving from SimpleInjector to .NET's built-in DI (with Scrutor for advanced registration), you absolutely can replicate that conditional fallback behavior for your validators—you just need to approach it a bit differently since native DI doesn't have a direct equivalent to SimpleInjector's RegisterConditional method. Here's a clean, maintainable way to achieve the same result:

Core Idea

Leverage .NET DI's built-in behavior where concrete service registrations take priority over open-generic registrations. We'll first register our fallback NullValidator<T> as an open generic, then scan for and register all custom IValidator<T> implementations. This way, if a custom validator exists for a given T, it will be used; if not, the NullValidator<T> will be injected as a fallback.

Step 1: Register the Fallback NullValidator

Start by registering your NullValidator<T> as an open-generic transient service. This will be our default when no custom validator exists:

services.AddTransient(typeof(IValidator<>), typeof(NullValidator<>));

Step 2: Scan for Custom Validators with Scrutor

Use Scrutor's scanning capabilities to automatically register all your custom IValidator<T> implementations (those inheriting from AbstractValidator<T>) across your assemblies:

services.Scan(scan => scan
    .FromAssembliesOf(typeof(YourAssemblyMarker)) // Replace with a type from your validator assembly
    .AddClasses(classes => classes.AssignableTo(typeof(IValidator<>)))
    .AsImplementedInterfaces()
    .WithTransientLifetime());

Since these are concrete, closed-generic registrations, they will override the open-generic NullValidator<T> for their specific T types.

Step 3: Register Command/Query Handlers and Decorators

Next, register your command/query handlers and apply the FluentValidation decorator using Scrutor:

// Register all command handlers
services.Scan(scan => scan
    .FromAssembliesOf(typeof(CreateUserCommandHandler)) // Replace with a command handler type
    .AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)))
    .AsImplementedInterfaces()
    .WithTransientLifetime());

// Apply the FluentValidation decorator to all command handlers
services.Decorate(typeof(ICommandHandler<>), typeof(FluentValidationCommandHandlerDecorator<>));

// Repeat the same pattern for query handlers if needed
services.Scan(scan => scan
    .FromAssembliesOf(typeof(GetUserQueryHandler)) // Replace with a query handler type
    .AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>)))
    .AsImplementedInterfaces()
    .WithTransientLifetime());

// Add your query validation decorator here if you have one
// services.Decorate(typeof(IQueryHandler<,>), typeof(FluentValidationQueryHandlerDecorator<,>));

How It Works

When the DI container resolves an IValidator<TCommand> for your decorator:

  • If a custom IValidator<TCommand> exists (e.g., CreateUserCommandValidator), that concrete implementation is returned.
  • If no custom validator exists, the open-generic registration kicks in, creating and returning a NullValidator<TCommand> instance.

This matches exactly the behavior you had with SimpleInjector's conditional registration—your decorator will always receive a non-null IValidator<T> instance, eliminating the need for null checks (though your existing null guard clauses are still good practice!).

Bonus: Verify the Behavior

To confirm this works as expected, you can test resolving a validator for a command with no custom validator:

var validator = serviceProvider.GetRequiredService<IValidator<NoValidationCommand>>();
Debug.Assert(validator is NullValidator<NoValidationCommand>);

内容的提问来源于stack exchange,提问作者M.E.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:27:46