如果有帮助,我将一个小属性和助手放在一起,以使其更具动态性。
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);
});
}