c# - 如何验证字典中的项目使用 FluentValidation 查看模型属性
问题描述
我正在使用Dictionary<string, T>
数据结构将电话号码存储在视图模型中。用户可以在客户端添加或删除它们,然后再将它们发送回服务器。
背景故事:我正在使用字典,因为List<T>
在 ASP.NET MVC 5 中使用数据结构需要表单字段的名称包含从零开始的顺序索引,并且 JavaScript 在屏幕上添加或删除这些字段变得非常痛苦无需重新排序索引值。我发现使用字典很容易。现在我正在做一个概念验证任务来启用依赖注入,它允许我们在验证期间使用我们的 NHibernate 会话来查询数据库,并使用控制器和视图模型使用的相同会话而不是 FluentValidation 的“单例”模式与 MVC 5 一起使用。
当使用[Validator(typeof(T))]
视图模型上面的属性时,字段显示的消息很好,但是验证器实例是 AppDomain 中的单例,验证器使用的 NHibernate 会话与控制器使用的不同。这会导致数据在数据验证期间变得不同步。检查数据库的验证开始返回意外结果,因为 NHibernate 在服务器上缓存了如此多的数据,并且它实际上有 2 个单独的缓存。
项目设置
- ASP.NET MVC 5
- .NET Framework 4.5.1(但我们可以升级)
- FluentValidation v8.5.0
- FluentValidation.Mvc5 v8.5.0
- FluentValidation.ValidatorAttribute v8.5.0
查看模型
public class PersonForm
{
public PhoneFieldsCollection Phones { get; set; }
}
public class PhoneFieldsCollection
{
public Dictionary<string, PhoneNumberFields> Items { get; set; }
}
public class PhoneNumberFields
{
[Display(Name="Country Code")]
[DataType(DataType.PhoneNumber)]
public string CountryCode { get; set; }
[Display(Name="Phone Number")]
[DataType(DataType.PhoneNumber)]
public string PhoneNumber { get; set; }
[DataType(DataType.PhoneNumber)]
public string Extension { get; set; }
[Display(Name="Type")]
public string TypeCode { get; set; }
}
查看模型验证器
public class PersonFormValidator : AbstractValidator<PersonForm>
{
private readonly IPersonRepository repository;
public PersonFormValidator(IPersonRepository repository)
{
// Later on in proof of concept I will need to query the database
this.repository = repository;
RuleForEach(model => model.Phones)
.SetValidator(new PhoneNumberFieldsValidator());
}
}
public class PhoneNumberFieldsValidator : AbstractValidator<PhoneNumberFields>
{
public PhoneNumberFieldsValidator()
{
RuleFor(model => model.PhoneNumber)
.NotEmpty();
}
}
验证视图模型的控制器代码:
private bool IsModelStateValid(PersonForm model)
{
// The `repository` field is an IPersonRepository object from the DI container
var validator = new PersonFormValidator(repository);
var results = validator.Validate(model);
if (results.IsValid)
return true;
results.AddToModelState(ModelState, "");
return false;
}
用于呈现页面的 Razor 模板代码
页面级模板
@model PersonForm
@Html.EditorFor(model => model.Phones)
PhoneFieldCollection 编辑器模板
@model PhoneFieldsCollection
<fieldset class="form-group form-group-phones">
<legend class="control-label col-md-3 required">
Phone Numbers:
</legend>
<div class="col-md-9">
@Html.ValidationMessageFor(model => model, "", new { role = "alert", @class = "alert alert-danger", @for = Html.IdFor(model => model) + "-addButton" })
<ol class="list-unstyled">
@foreach (var item in Model.Items)
{
if (item.Value.IsClientSideTemplate)
{
<script type="text/html">
@Html.EditorFor(model => model.Items[item.Key])
</script>
}
else
{
@Html.EditorFor(model => model.Items[item.Key])
}
}
</ol>
<hr />
<p>
<button type="button" class="btn btn-default" id="@Html.IdFor(model => model)-addButton"
data-dynamiclist-action="add"
data-dynamiclist="fieldset.form-group-phones ol">
<span class="glyphicon glyphicon-plus"></span>
Add another phone number
</button>
</p>
</div>
</fieldset>
PhoneNumberFields 编辑器模板
@model PhoneNumberFields
@Html.EditorFor(model => model.PhoneNumber)
@Html.ValidationMessageFor(model => model.PhoneNumber)
必填字段消息未显示
当我在电话号码字段为空的情况下将表单发布回服务器时,我在页面顶部收到一条验证摘要消息,上面写着“需要电话号码字段”,这是我所期望的。但是,编辑器模板中的调用ValidationMessageFor(model => model.PhoneNumber)
不会导致验证消息出现在表单字段中。
在调试模式下运行应用程序时,我得到Phones[0].PhoneNumber
了具有验证消息的字段的名称,但视图模型中的字段名称是Phones.Items[123].PhoneNumber
(其中 123 是数据库 ID,或new Date().getTime()
JavaScript 生成的时间戳)。
所以我知道为什么验证消息没有显示在字段旁边。挑战是,我该怎么做?
如何使用 FluentValidation 验证字典,以便ValidationMessageFor(model => model.PhoneNumber)
在编辑器模板中使用 ** 时错误消息出现在表单字段中?
更新:看起来 2017 年有一个与此相关的 GitHub 问题:支持 IDictionary Validation。该人找到了一种解决方法,但 FluentValidation 的维护者基本上说支持这是一个巨大的痛苦,需要进行重大的重构工作。如果我能得到一些工作,我可能会尝试自己摆弄这个并发布答案。
解决方案
推荐阅读
- javascript - 为 history.back 按钮定义一个参考站点
- python - 正则表达式去除 C 标识符周围的(双)下划线
- corda - 测试 CordaApp 更改的最佳(最快)方法是什么?
- javascript - 是否可以将 Angular JS 用于 JAMstack 架构?
- html - 制作响应式网站但图像太小?
- html - 如何将 html 验证的默认文本更改为自定义错误消息
- regex - 正则表达式可选词
- ansible - Ansible 在输入变量文件中传递多行
- android - 使用 SupportMapFragment 时应用更改按钮不起作用
- spring - 从 gwt 调用 Spring MicroService