.NET 9 Blazor Server端DataAnnotations元数据失效问题排查
问题原因
Blazor Server的组件(如EditForm、DataAnnotationsValidator)并不依赖TypeDescriptor系统获取元数据,而是直接通过反射读取DataAnnotations特性,或者基于ASP.NET Core的ModelMetadata体系工作。你之前使用TypeDescriptor.AddProviderTransparent的方式仅适用于WinForms、ASP.NET Web Forms等旧技术栈,对Blazor完全无效,这就是元数据不生效的核心原因。
解决方案
方案1:使用分部类 + [MetadataType]特性(最简洁推荐)
由于EF Core Power Tools生成的AsphaltIndexPrice是分部类,你可以创建一个新的分部类文件,通过[MetadataType]特性关联元数据类,Blazor会自动识别元数据中的注解:
// 新建文件:AsphaltIndexPrice.Metadata.cs using System.ComponentModel.DataAnnotations; [MetadataType(typeof(AsphaltIndexPriceMetaData))] public partial class AsphaltIndexPrice { // 无需添加任何额外代码,仅用于关联元数据类 }
同时删除所有与TypeDescriptor相关的初始化代码(包括HMADBMetaDataInit方法和Program.cs中的调用),Blazor的DataAnnotationsValidator会自动读取元数据类中的[Display]、[Required]等特性。
方案2:自定义ModelMetadataProvider(无需修改分部类)
如果不想修改分部类,可以自定义一个元数据提供器,手动将元数据类的注解合并到目标类上:
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using System.ComponentModel.DataAnnotations; public class CustomMetadataProvider : DataAnnotationsModelMetadataProvider { // 维护模型类与元数据类的映射关系 private readonly Dictionary<Type, Type> _metadataMappings = new() { { typeof(AsphaltIndexPrice), typeof(AsphaltIndexPriceMetaData) } }; protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName) { // 如果当前模型类有对应的元数据类,合并属性注解 if (_metadataMappings.TryGetValue(modelType, out var metadataType)) { var metadataProperty = metadataType.GetProperty(propertyName); if (metadataProperty != null) { attributes = attributes.Concat(metadataProperty.GetCustomAttributes()); } } return base.CreateMetadata(attributes, containerType, modelType, propertyName); } }
然后在Program.cs中注册这个自定义提供器:
builder.Services.AddSingleton<IModelMetadataProvider, CustomMetadataProvider>(); // 后续的builder.Build()、app.Run()保持不变
方案3:使用FluentValidation(更灵活的验证方案)
如果需要更复杂的验证逻辑,可以使用FluentValidation替代DataAnnotations,它不需要依赖元数据类,直接编写验证规则:
- 安装NuGet包:
FluentValidation.AspNetCore - 编写验证规则:
using FluentValidation; public class AsphaltIndexPriceValidator : AbstractValidator<AsphaltIndexPrice> { public AsphaltIndexPriceValidator() { RuleFor(x => x.EffectiveDate) .NotEmpty().WithMessage("An Effective Date is required") .WithName("Effective Date"); RuleFor(x => x.IndexPrice) .NotEmpty().WithMessage("An Index Price is required") .WithName("Price"); } }
- 在
Program.cs注册验证器:
builder.Services.AddFluentValidationAutoValidation() .AddValidatorsFromAssemblyContaining<AsphaltIndexPriceValidator>();
验证步骤
- 确保页面的
EditForm中包含对应验证组件:
<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit"> <!-- 方案1/2使用 --> <DataAnnotationsValidator /> <!-- 方案3使用 --> <!-- <FluentValidationValidator /> --> <div class="form-group"> <label>@Html.DisplayNameFor(m => m.EffectiveDate)</label> <InputDate @bind-Value="@Model.EffectiveDate" /> <ValidationMessage For="@(() => Model.EffectiveDate)" /> </div> <!-- 其他表单字段 --> <button type="submit">Save</button> </EditForm>
- 重启项目后,标签会显示自定义名称,验证时会触发自定义错误提示。
内容的提问来源于stack exchange,提问作者StuartV
相关产品推荐
相关产品推荐

