Asp.net mvc 3 MVC HtmlHelper vs FluentValidation 3.1:获取模型元数据时遇到的问题
我为标签创建了一个HtmlHelper,如果需要关联字段,则该标签的名称后面会有一个星号:Asp.net mvc 3 MVC HtmlHelper vs FluentValidation 3.1:获取模型元数据时遇到的问题,asp.net-mvc-3,html-helper,fluentvalidation,modelmetadata,isrequired,Asp.net Mvc 3,Html Helper,Fluentvalidation,Modelmetadata,Isrequired,我为标签创建了一个HtmlHelper,如果需要关联字段,则该标签的名称后面会有一个星号: publicstaticmvchtmlstringlabelforr( 此HtmlHelper(html,表达式) { 返回标签助手( html, ModelMetadata.FromLambdaExpression(表达式,html.ViewData), ExpressionHelper.GetExpressionText(表达式), 无效); } 私有静态MvcHtmlString LabelHelp
publicstaticmvchtmlstringlabelforr(
此HtmlHelper(html,表达式)
{
返回标签助手(
html,
ModelMetadata.FromLambdaExpression(表达式,html.ViewData),
ExpressionHelper.GetExpressionText(表达式),
无效);
}
私有静态MvcHtmlString LabelHelper(HtmlHelper帮助程序、模型元数据、字符串htmlFieldName、字符串文本)
{
…//检查元数据。此处需要
…//如果需要,显示星号
}
如果我在ViewModel中的属性上使用DataAnnotations和slap[Required],则我的私有LabelHelper中的metadata.IsRequired将等于True,并且一切都将按预期工作
但是,如果我使用FluentValidation 3.1并添加这样一个简单的规则:
公共类CheckEmailViewModelValidator:AbstractValidator
{
公共检查EmailViewModelValidator()
{
规则(m=>m.Email)
.NotNull()
.EmailAddress();
}
}
。。。在我的LabelHelper中,metadata.IsRequired将被错误地设置为false。(不过验证器可以工作:您不能提交空字段,它需要像电子邮件一样)。其余元数据看起来是正确的(例如:metadata.DisplayName=“Email”)。
理论上,如果使用Rule.NotNull(),FluentValidator将在属性上slaprequiredAttribute 供参考: 我的ViewModel:
[Validator(typeof(CheckEmailViewModelValidator))]
公共类CheckEmailViewModel
{
//[必需]
[显示(Name=“电子邮件”)]
公共字符串电子邮件{get;set;}
}
我的控制器:
公共类成员控制器:控制器
{
[HttpGet]
公共行动结果检查电子邮件()
{
var model=新的CheckEmailViewModel();
返回视图(模型);
}
}
非常感谢您的帮助。默认情况下,MVC将DataAnnotations属性用于两个不同的目的—元数据和验证
当您在MVC应用程序中启用FluentValidation时,FluentValidation将钩住验证基础结构而不是元数据,MVC将继续使用元数据的属性。如果您想对元数据和验证使用FluentValidation,那么您需要编写MVC的ModelMetadataProvider的自定义实现,该实现知道如何查询验证器类-这不是FluentValidation现成支持的功能。我有一个自定义ModelMetadataProvider,可以增强默认值DataAnnotations提供了以下内容:
仅限于杰里米的评论。我没有写“MVC的ModelMetadataProvider的自定义实现,它知道如何查询验证器类”,基本上是因为我不知道如何立即执行,研究它可能需要很多时间。如果你能提供一个这样的例子,那绝对有帮助!我也不知道。我扩展了LabelFor并向其添加了类。这不是最好的解决方案,但我没有时间研究如何实现ModelMetadataProvider。但如果我这样做了,我会在这里写解决方案。传递给
CustomModelMetaDataProvider
的构造函数中的IValidatorFactory
是什么?@skeletank这是一个流畅的验证抽象。正如我所记得的,它用于解析中的验证器
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
readonly IValidatorFactory factory;
public CustomModelMetadataProvider(IValidatorFactory factory)
: base() {
this.factory = factory;
}
// Uppercase followed by lowercase but not on existing word boundary (eg. the start)
Regex _camelCaseRegex = new Regex(@"\B\p{Lu}\p{Ll}", RegexOptions.Compiled);
// Creates a nice DisplayName from the model’s property name if one hasn't been specified
protected override ModelMetadata GetMetadataForProperty(
Func<object> modelAccessor,
Type containerType,
PropertyDescriptor propertyDescriptor) {
ModelMetadata metadata = base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor);
metadata.IsRequired = metadata.IsRequired || IsNotEmpty(containerType, propertyDescriptor.Name);
if (metadata.DisplayName == null)
metadata.DisplayName = displayNameFromCamelCase(metadata.GetDisplayName());
if (string.IsNullOrWhiteSpace(metadata.DisplayFormatString) &&
(propertyDescriptor.PropertyType == typeof(DateTime) || propertyDescriptor.PropertyType == typeof(DateTime?))) {
metadata.DisplayFormatString = "{0:d}";
}
return metadata;
}
string displayNameFromCamelCase(string name) {
name = _camelCaseRegex.Replace(name, " $0");
if (name.EndsWith(" Id"))
name = name.Substring(0, name.Length - 3);
return name;
}
bool IsNotEmpty(Type type, string name) {
bool notEmpty = false;
var validator = factory.GetValidator(type);
if (validator == null)
return false;
IEnumerable<IPropertyValidator> validators = validator.CreateDescriptor().GetValidatorsForMember(name);
notEmpty = validators.OfType<INotNullValidator>().Cast<IPropertyValidator>()
.Concat(validators.OfType<INotEmptyValidator>().Cast<IPropertyValidator>()).Count() > 0;
return notEmpty;
}
}