You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ASP.NET Core自定义验证属性注入服务及单元测试方案咨询

Nice question! Let's tackle both parts of your problem step by step:

Injecting Services into Your Custom Validation Attribute in ASP.NET Core

First off, the approach you're already using—validationContext.GetService(typeof(ILocalizer))—is the right way to go here. Unlike regular services, custom ValidationAttribute instances are often cached by the framework, so you can't inject dependencies directly via the constructor (that would cause lifecycle issues). Instead, leveraging the ValidationContext to pull services from the DI container is the recommended pattern.

To make this work reliably, just ensure two things:

  • You've properly registered the ILocalizer service in your app's configuration (Program.cs or Startup.cs):
    builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
    
  • Don't cache the ILocalizer instance you retrieve. Fetch it fresh every time the IsValid method runs to avoid problems with service scoping.

Your existing code already follows this pattern, so as long as the ILocalizer is registered, it should work as expected in your ASP.NET Core app.

Configuring ValidationContext for Unit Testing

When writing unit tests, you don't have the full ASP.NET Core DI container available, so you need to manually mock the ValidationContext and the services it needs to resolve. Here's how to do it with Moq (a popular mocking library), or with a simple custom implementation if you prefer not to use Moq:

Option 1: Using Moq

using Moq;
using Microsoft.Extensions.Localization;
using System.ComponentModel.DataAnnotations;

// 1. Mock the ILocalizer to return your test error message
var mockLocalizer = new Mock<ILocalizer>();
mockLocalizer.Setup(l => l.GetString(It.IsAny<string>()))
             .Returns("Test error message for invalid age");

// 2. Mock the IServiceProvider to return your mocked ILocalizer
var mockServiceProvider = new Mock<IServiceProvider>();
mockServiceProvider.Setup(sp => sp.GetService(typeof(ILocalizer)))
                   .Returns(mockLocalizer.Object);

// 3. Create your test model (adjust to match your actual model type)
var testModel = new YourModel { DateOfBirth = DateTime.UtcNow.AddYears(-16) }; // Triggers "too young" validation

// 4. Build the ValidationContext with your mocked service provider
var validationContext = new ValidationContext(testModel, mockServiceProvider.Object, null);

// 5. Run the validation
var ageValidator = new YourCustomAgeValidationAttribute();
var validationResult = ageValidator.GetValidationResult(testModel.DateOfBirth, validationContext);

// 6. Assert the result
Assert.NotNull(validationResult);
Assert.Equal("Test error message for invalid age", validationResult.ErrorMessage);

Option 2: Custom IServiceProvider Implementation (No Moq)

If you don't want to use a mocking library, you can create a simple test-only service provider:

using Microsoft.Extensions.Localization;
using System.ComponentModel.DataAnnotations;

// Create a test ILocalizer implementation or use a mock
var testLocalizer = new Mock<ILocalizer>().Object; // Or implement a basic ILocalizer for testing

// Custom service provider that returns our test ILocalizer
public class TestServiceProvider : IServiceProvider
{
    private readonly ILocalizer _localizer;

    public TestServiceProvider(ILocalizer localizer) => _localizer = localizer;

    public object GetService(Type serviceType)
    {
        return serviceType == typeof(ILocalizer) ? _localizer : null;
    }
}

// Then use it in your test:
var serviceProvider = new TestServiceProvider(testLocalizer);
var validationContext = new ValidationContext(new YourModel(), serviceProvider, null);
// ... rest of the test logic as above

Either approach will let you simulate the DI container in your unit tests and verify that your validation logic works as expected, including the localization of error messages.

内容的提问来源于stack exchange,提问作者Coder949

火山引擎 最新活动