You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义合并属性未被Entity Framework迁移生成器识别的解决方案咨询

解决方案:统一自定义属性满足Blazor验证与EF迁移需求

核心思路

通过自定义聚合属性同时实现Blazor表单验证逻辑,并让EF通过约定扫描该属性的配置,自动生成数据库约束,彻底避免重复配置的不一致问题。


步骤1:定义聚合验证属性

创建继承ValidationAttribute的自定义属性,封装所有需要的验证规则(必填、长度、正则等),同时暴露EF需要的元数据(如最大长度、是否必填):

using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ShortStringFieldAttribute : ValidationAttribute
{
    public int MinLength { get; } = Constants.ShortStringFieldMinLength;
    public int MaxLength { get; } = Constants.ShortStringFieldMaxLength;
    public string RegEx { get; } = Constants.ShortNameRegEx;
    public string DisplayName { get; }

    public ShortStringFieldAttribute(string displayName)
    {
        DisplayName = displayName;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var strValue = value as string;

        // 必填验证
        if (string.IsNullOrEmpty(strValue))
            return new ValidationResult($"请输入{DisplayName}");

        // 最小长度验证
        if (strValue.Length < MinLength)
            return new ValidationResult($"{DisplayName}长度不能少于{MinLength}个字符");

        // 最大长度验证
        if (strValue.Length > MaxLength)
            return new ValidationResult($"{DisplayName}长度不能超过{MaxLength}个字符");

        // 正则格式验证
        if (!Regex.IsMatch(strValue, RegEx))
            return new ValidationResult($"{DisplayName}格式不符合要求");

        return ValidationResult.Success;
    }
}

// 同理定义长文本字段属性
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class LongStringFieldAttribute : ValidationAttribute
{
    public int MinLength { get; } = Constants.LongStringFieldMinLength;
    public int MaxLength { get; } = Constants.LongStringFieldMaxLength;
    public string RegEx { get; } = Constants.LongStringFieldRegEx;
    public string DisplayName { get; }

    public LongStringFieldAttribute(string displayName)
    {
        DisplayName = displayName;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var strValue = value as string;

        if (string.IsNullOrEmpty(strValue))
            return new ValidationResult($"请输入{DisplayName}");

        if (strValue.Length < MinLength)
            return new ValidationResult($"{DisplayName}长度不能少于{MinLength}个字符");

        if (strValue.Length > MaxLength)
            return new ValidationResult($"{DisplayName}长度不能超过{MaxLength}个字符");

        if (!Regex.IsMatch(strValue, RegEx))
            return new ValidationResult($"{DisplayName}格式不符合要求");

        return ValidationResult.Success;
    }
}

步骤2:配置EF约定识别自定义属性

在DbContext的OnModelCreating方法中,扫描实体属性的自定义属性,自动应用数据库约束:

using Microsoft.EntityFrameworkCore;

public class YourDbContext : DbContext
{
    // 定义DbSet...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 遍历所有实体及属性
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            foreach (var property in entityType.GetProperties())
            {
                // 处理短文本字段属性
                var shortAttr = property.PropertyInfo?.GetCustomAttribute<ShortStringFieldAttribute>();
                if (shortAttr != null)
                {
                    property.IsRequired = true;
                    property.SetMaxLength(shortAttr.MaxLength);
                }

                // 处理长文本字段属性
                var longAttr = property.PropertyInfo?.GetCustomAttribute<LongStringFieldAttribute>();
                if (longAttr != null)
                {
                    property.IsRequired = true;
                    property.SetMaxLength(longAttr.MaxLength);
                }
            }
        }

        base.OnModelCreating(modelBuilder);
    }
}

步骤3:配置Blazor识别自定义属性的DisplayName

注册自定义IDisplayMetadataProvider,让Blazor表单能读取自定义属性的DisplayName:

// 在Program.cs中添加服务注册
builder.Services.AddSingleton<IDisplayMetadataProvider, CustomDisplayMetadataProvider>();

// 自定义DisplayMetadataProvider实现
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

public class CustomDisplayMetadataProvider : IDisplayMetadataProvider
{
    public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
    {
        var shortAttr = context.Attributes.OfType<ShortStringFieldAttribute>().FirstOrDefault();
        if (shortAttr != null)
        {
            context.DisplayMetadata.DisplayName = () => shortAttr.DisplayName;
        }

        var longAttr = context.Attributes.OfType<LongStringFieldAttribute>().FirstOrDefault();
        if (longAttr != null)
        {
            context.DisplayMetadata.DisplayName = () => longAttr.DisplayName;
        }
    }
}

最终使用方式

直接在实体属性上标记自定义属性即可,无需重复添加验证和EF配置:

[ShortStringField("名称")]
public string Name { get; set; } = string.Empty;

[LongStringField("描述")]
public string Description { get; set; } = string.Empty;

这样配置后:

  • Blazor表单会自动触发自定义属性内的验证逻辑
  • EF迁移时会自动生成NOT NULL和MAX_LENGTH约束
  • 表单会正确显示自定义的DisplayName

内容的提问来源于stack exchange,提问作者Andrea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 14:20:37