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

EF Core中DeniedValues验证属性结合StringComparison无效,求解决方案

解决方案:EF Core中DeniedValues属性StringComparison参数失效问题

问题原因

EF Core内置的DeniedValuesAttribute并未正确处理StringComparison参数,服务器端验证(如SaveChanges阶段)仅执行大小写敏感的匹配逻辑,完全忽略传入的比较规则,导致指定的不区分大小写等验证不生效。

可行解决方案

1. 自定义支持StringComparison的验证属性

继承ValidationAttribute实现自定义逻辑,完全控制值的比较规则:

public class DeniedValuesWithComparisonAttribute : ValidationAttribute
{
    private readonly string[] _deniedValues;
    private readonly StringComparison _comparisonType;

    // 适配原属性的参数传递方式
    public DeniedValuesWithComparisonAttribute(StringComparison comparisonType, params string[] deniedValues)
    {
        _deniedValues = deniedValues;
        _comparisonType = comparisonType;
        ErrorMessage = "字段 {0} 包含禁用值。";
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (value is string strValue && !string.IsNullOrEmpty(strValue))
        {
            foreach (var deniedVal in _deniedValues)
            {
                if (strValue.Equals(deniedVal, _comparisonType))
                {
                    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
                }
            }
        }
        return ValidationResult.Success;
    }
}

使用方式替换原属性:

[DeniedValuesWithComparison(StringComparison.OrdinalIgnoreCase, "bad", "lol")]
public string Name { get; set; } = "";

2. 重写DbContext的SaveChanges方法手动验证

在数据保存前遍历实体,检查目标属性值:

public class YourDbContext : DbContext
{
    // 构造函数及DbSet定义省略

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        ValidateDeniedNameValues();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
    {
        ValidateDeniedNameValues();
        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    private void ValidateDeniedNameValues()
    {
        var deniedValues = new[] { "bad", "lol" };
        foreach (var entry in ChangeTracker.Entries()
            .Where(e => e.State is EntityState.Added or EntityState.Modified))
        {
            var nameProp = entry.Entity.GetType().GetProperty(nameof(Name));
            if (nameProp?.GetValue(entry.Entity) is string nameValue && !string.IsNullOrEmpty(nameValue))
            {
                if (deniedValues.Any(v => nameValue.Equals(v, StringComparison.OrdinalIgnoreCase)))
                {
                    throw new ValidationException($"名称包含禁用值:{nameValue}");
                }
            }
        }
    }
}

3. 使用Fluent API配置验证规则

在实体配置中添加自定义验证回调:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<YourEntity>()
        .Property(e => e.Name)
        .HasValidationCallback((entity, value) =>
        {
            var strValue = value as string;
            var deniedValues = new[] { "bad", "lol" };
            if (!string.IsNullOrEmpty(strValue) && deniedValues.Any(v => strValue.Equals(v, StringComparison.OrdinalIgnoreCase)))
            {
                throw new ValidationException($"名称不能为 '{strValue}'。");
            }
        });
}

补充说明

如果需要客户端验证(如ASP.NET MVC/Razor Pages场景),自定义验证属性还需实现IClientValidatable接口,生成对应的客户端验证脚本;若仅需服务器端数据校验,上述方案已足够覆盖需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 19:32:56