MVC ViewModel如何让LabelFor/ValidationFor获取自定义动态显示名
动态获取ViewModel属性作为显示名的解决方案
我之前碰到过一模一样的场景——默认的DisplayNameAttribute只能用静态常量,完全没法满足这种从ViewModel自身属性读取动态显示名的需求。不过咱们可以通过自定义模型元数据提供器来解决这个问题,具体步骤如下:
1. 创建自定义标记属性
首先定义一个Attribute,用来标记当前字段对应的显示名字段名称:
public class DynamicDisplayNameAttribute : Attribute { public string DisplayNamePropertyName { get; } public DynamicDisplayNameAttribute(string displayNamePropertyName) { DisplayNamePropertyName = displayNamePropertyName; } }
2. 自定义模型元数据提供器
这个类负责在运行时读取ViewModel实例里的显示名字段值,替换默认的DisplayName:
public class DynamicDisplayNameMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { // 先获取默认的元数据 var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); // 查找咱们自定义的Attribute var dynamicAttr = attributes.OfType<DynamicDisplayNameAttribute>().FirstOrDefault(); if (dynamicAttr != null && modelAccessor != null) { // 获取ViewModel的实例 var container = modelAccessor.Target; if (container != null) { var viewModel = container.GetType().GetProperty("Model")?.GetValue(container); if (viewModel != null) { // 读取指定显示名字段的值 var displayName = viewModel.GetType() .GetProperty(dynamicAttr.DisplayNamePropertyName)? .GetValue(viewModel)? .ToString(); // 如果获取到有效值,就替换DisplayName if (!string.IsNullOrEmpty(displayName)) { metadata.DisplayName = () => displayName; } } } } return metadata; } }
3. 注册自定义提供器
ASP.NET MVC(传统框架)
在Global.asax.cs的Application_Start方法里注册:
protected void Application_Start() { // 其他启动配置... ModelMetadataProviders.Current = new DynamicDisplayNameMetadataProvider(); }
ASP.NET Core
在Startup.cs的ConfigureServices方法里注册:
services.AddMvc(options => { options.ModelMetadataDetailsProviders.Add(new DynamicDisplayNameMetadataProvider()); });
4. 修改ViewModel使用自定义属性
现在把原来的DisplayNameAttribute换成咱们的自定义属性:
public class MyViewModel { [DynamicDisplayName(nameof(UserField1Name))] public string UserField1 { get; set; } public string UserField1Name { get; set; } [DynamicDisplayName(nameof(UserField2Name))] public string UserField2 { get; set; } public string UserField2Name { get; set; } }
原理说明
默认的DataAnnotationsModelMetadataProvider只会读取编译时固定的Attribute值,而咱们的自定义提供器会在运行时拿到ViewModel的实际实例,反射读取指定字段的动态值,然后把这个值设置为字段的DisplayName。这样LabelFor和ValidationMessageFor就会自动使用这个动态显示名了。
如果显示名字段为空,还可以加个 fallback逻辑,比如用属性名或者默认的DisplayName,只需要在代码里判断一下就行。
内容的提问来源于stack exchange,提问作者Maya
相关产品推荐
相关产品推荐

