FluentValidation.NET 等效于 [Display(Name)]

Posted

技术标签:

【中文标题】FluentValidation.NET 等效于 [Display(Name)]【英文标题】:FluentValidation.NET equivalent to [Display(Name)] 【发布时间】:2017-05-12 03:11:15 【问题描述】:

在FluentValidation.NET 之前,我可以像这样给一个自定义标签:

[Display(Name="Blah")]
public string BlahBlahBlah  get; set; 

我可以通过多种方式使用它:

@html.LabelFor(m => m.BlahBlahBlah)
@Html.DisplayNameFor(m => m.BlahBlahBlah)

<label asp-for="BlahBlahBlah"></label>

现在我想从我的模型中删除所有数据注释,并转向流畅的验证。在我的验证器中,我有这个:

RuleFor(o => o.BlahBlahBlah)
    .NotEmpty()
    .WithName("Blah");

但这不起作用。为什么?

【问题讨论】:

【参考方案1】:

FluentValidation 中的WithName 方法仅用于调整验证错误消息,如果您想替换 C# 属性名称以更加用户友好(请参阅 Overriding the Default Property Name 了解详细信息)。

所以答案是 - 一般情况下,您不能将 Display 替换为 WithName(),仅用于验证错误消息。

【讨论】:

有没有办法使用 Fluent Validation 添加显示? 不,这个库只用于验证。 这是令人沮丧的信息。现在我希望这个库被称为“Fluent Annotation”! 我想摆脱Display 属性的唯一简单选择是直接在Razor 中指定显示文本,其中一个重载为LabelFor。另一种方法是编写您的自定义 MyLabelFor 扩展方法,您可以在其中从任何您想要的来源获取文本。【参考方案2】:

如果有帮助,我将一个小属性和助手放在一起,以使其更具动态性。

WithDisplayNameAttribute.cs

[AttributeUsage(AttributeTargets.Property)]
public class WithDisplayNameAttribute : Attribute

    public WithDisplayNameAttribute(string displayName)
    
        DisplayName = displayName;
    

    /// <summary>
    /// The preferred friendly name to display for this property during validation.
    /// </summary>
    public string DisplayName  get; set; 

WithDisplayNameHelper.cs

internal static class WithDisplayNameHelper

    public static IReadOnlyDictionary<string, string> Map  get; 

    static WithDisplayNameHelper()
    
        var core = typeof(WithDisplayNameHelper).Assembly;
        var map = new Dictionary<string, string>();
        foreach (var parentModelType in core.GetExportedTypes().Where(x => x.IsClass))
        
            foreach (var prop in parentModelType.GetProperties())
            
                var att = prop.GetCustomAttribute<WithDisplayNameAttribute>();
                if (att == null) continue;
                var key = GetKey(parentModelType, prop);
                if (!map.ContainsKey(key))
                
                    map.Add(key, att.DisplayName);
                
            
        

        Map = new ReadOnlyDictionary<string, string>(map);
    

    /// <summary>
    /// Gets the key to use for this property.
    /// </summary>
    /// <param name="parent">The parent class containing the property.</param>
    /// <param name="prop">The property described by the display name.</param>
    /// <returns></returns>
    private static string GetKey(Type parent, PropertyInfo prop) => GetKey(parent, prop.Name);
    
    /// <inheritdoc cref="GetKey(System.Type,System.Reflection.PropertyInfo)"/>
    private static string GetKey(Type parent, string prop) => $"parent.FullName.prop";

    /// <summary>
    /// Retrieves the display name if one was set using the <see cref="WithDisplayNameAttribute"/>. Otherwise will return the given <paramref name="propertyName"/>.
    /// </summary>
    /// <param name="parent">The parent class containing the property.</param>
    /// <param name="propertyName">The property name.</param>
    /// <returns></returns>
    public static string GetDisplayNameOrDefault(Type parent, string propertyName) =>
        Map.TryGetValue(GetKey(parent, propertyName), out var value)
        ? value
        : propertyName;

    /// <summary>
    /// Attempts to retrieve the display name if one was set using the <see cref="WithDisplayNameAttribute"/>.
    /// </summary>
    /// <inheritdoc cref="GetDisplayNameOrDefault"/>
    /// <returns></returns>
    public static bool TryGetDisplayName(Type parent, string propertyName, out string result) =>
        Map.TryGetValue(GetKey(parent, propertyName), out result);



以及扩展方法:

    /// <summary>
    /// Specifies a custom property name to use within the error message.
    /// </summary>
    /// <param name="rule">The current rule</param>
    /// <returns></returns>
    public static IRuleBuilderOptions<T, TProperty> WithDisplayName<T, TProperty>(
        this IRuleBuilderOptions<T, TProperty> rule)
    
        
        return rule.Configure(x =>
        
            if (WithDisplayNameHelper.TryGetDisplayName(typeof(T), x.PropertyName, out var displayName))
                x.DisplayName = new StaticStringSource(displayName);
        );
    

【讨论】:

以上是关于FluentValidation.NET 等效于 [Display(Name)]的主要内容,如果未能解决你的问题,请参考以下文章

翻译FluentValidation验证组件的使用

C# 等效于 Java 标点正则表达式

等效于 Riverpod 中的 ChangeNotifierProvider 小部件

NSTimer 等效于 Javascript

复合赋值运算符

linq 等效于通用函数的'select *' sql?