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

如何让FluentValidation验证字典时用键而非数组索引作属性名

字典验证属性名格式问题

这是针对.NET 4、ASP.NET 5与FluentValidation 8.5旧问题的后续提问。尽管可能被视为重复,但FluentValidation多年来变化较大,且本文有更简单的复现方式。另可参考GitHub上已有8年历史的相关议题,该议题提及要“更好地支持字典”。

我正在尝试使用FluentValidation 11.9.1验证字典。.NET中的字典虽实现了IEnumerable<KeyValuePair<T1, T2>>,但理应通过索引符号(如items["foo"])访问。以下是复现问题的最简代码,后续会说明实际应用中必须验证字典的原因。

环境信息

-.NET Framework 8
-Visual Studio 2022中的MS Test项目
-FluentValidation 11.9.1

模型代码

public class OrderForm
{
    public Dictionary<string, OrderItem> Items { get; set; } = new Dictionary<string, OrderItem>();
}

public class OrderItem
{
    public string? Name { get; set; }
    public decimal? Price { get; set; }
}

Items字典的string类型键是必需的,因为最终要用于单体MVC应用,JavaScript会动态向列表中添加项(后续详述)。

验证器代码

public class OrderFormValidator : AbstractValidator<OrderForm>
{
    public OrderFormValidator()
    {
        RuleFor(model => model.Items)
            .NotEmpty();

        RuleForEach(model => model.Items)
            .SetValidator(new OrderItemValidator());
    }
}

public class OrderItemValidator : AbstractValidator<KeyValuePair<string, OrderItem>>
{
    public OrderItemValidator()
    {
        RuleFor(model => model.Value.Name)
            .NotEmpty();

        RuleFor(model => model.Value.Price)
            .NotNull()
            .GreaterThan(0.0m);
    }
}

失败的测试用例

以下测试展示了预期结果:

[TestClass]
public class OrderFormValidationTests
{
    [TestMethod]
    public void Given_empty_order_item_When_validating_Then_order_item_is_invalid()
    {
        var model = new OrderForm()
        {
            Items = new Dictionary<string, OrderItem>()
            {
                ["foo"] = new OrderItem()
            }
        };
        var validator = new OrderFormValidator();
        var results = validator.Validate(model);

        // 此断言通过
        results.Errors.Count.ShouldBeEqualTo(2);

        // 此断言失败
        results.Errors[0].PropertyName.ShouldBeEqualTo(@"Items[foo].Price");
    }
}

测试失败信息:

Given_empty_order_item_When_validating_Then_order_item_is_invalid
   Source: UnitTest1.cs line 10
   Duration: 349 ms

  Message: 
Test method OrderFormValidationTests.Given_empty_order_item_When_validating_Then_order_item_is_invalid threw exception: 
FluentAssert.Exceptions.ShouldBeEqualAssertionException:   Expected string length 18 but was 19. Strings differ at index 6.
  Expected: "Items[foo].Price"
  But was:  "Items[0].Value.Name"

为何在模型中使用字典

Visual Studio智能提示显示RuleForEach(model => model.Items)期望接收IEnumerable<KeyValuePair<string, OrderItem>>,这在技术上是正确的,因为Dictionary<T1, T2>实现了IEnumerable<T>。但FluentValidation为字典项生成的属性名会破坏ASP.NET MVC模型与Razor视图中的验证消息绑定。Razor视图中调用@Html.EditorFor(model => model.Items["foo"].Name)会生成格式为Items[foo].Name的表单字段名,ValidationMessageFor助手期望MVC ModelState中存在同名的验证错误,但FluentValidation却使用数字索引(如同List或数组)而非字典键。

遗憾的是,我的应用视图层必须使用字典。这是一个老旧的MVC 5单体应用,旧版FluentValidation验证器特性可正确关联字典模型的验证消息,因此在.NET 4/MVC 5/FluentValidation 8.5环境下可正常工作。客户端有JavaScript会在页面加载后动态添加项,我需要将HTML模板中的特殊令牌替换为Date.now()以生成字典的新键,确保表单完整回传。由于表单庞大复杂,无法完全重构为其他数据结构,必须使用字典。

我的问题

如何让FluentValidation验证字典时,属性名格式为Items[foo].Name而非Items[0].Name?

理想的解决方案优先级如下:
1.(最优)提供可实现该效果的API调用序列;
2.编写自定义验证器或重写FluentValidation的部分逻辑以生成所需属性名;
3.重构C#验证器结构,但需保留字典的使用;
4.说明该功能暂不支持,需提交PR实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:25:55