自定义Unique校验属性中ValidationContext始终为Null的解决方法
修复自定义UniqueAttribute中validationContext为Null的问题
问题原因
validationContext为Null通常是因为验证触发时未传入依赖注入上下文:要么是手动调用验证方法时省略了ValidationContext参数,要么是验证流程未在ASP.NET Core的依赖注入容器环境中执行。即使你设置了RequiresValidationContext = true,如果验证触发方式不正确,框架也不会传入上下文。
核心修复步骤
1. 完善UniqueAttribute的实现(确保能正确获取上下文与服务)
修改自定义Attribute,通过validationContext解析DbContext实现唯一性校验,同时明确指定要校验的实体和属性:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)] public class UniqueAttribute : ValidationAttribute { private readonly Type _dbContextType; private readonly string _entitySetName; private readonly string _targetPropertyName; // 构造函数:指定DbContext类型、实体集合名称、要校验的属性名 public UniqueAttribute(Type dbContextType, string entitySetName, string targetPropertyName) { _dbContextType = dbContextType; _entitySetName = entitySetName; _targetPropertyName = targetPropertyName; ErrorMessage = $"该{_targetPropertyName}已被占用"; } public override bool RequiresValidationContext => true; protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value == null) return ValidationResult.Success; // 从validationContext获取DbContext实例 var dbContext = validationContext.GetService(_dbContextType); if (dbContext == null) return new ValidationResult("无法获取数据库上下文"); // 获取对应的实体集合 var dbSet = dbContext.GetType().GetProperty(_entitySetName)?.GetValue(dbContext) as IQueryable; if (dbSet == null) return new ValidationResult($"找不到实体集合{_entitySetName}"); // 通过表达式树构建查询,检查值是否存在 var entityType = dbSet.ElementType; var parameter = Expression.Parameter(entityType, "entity"); var property = Expression.Property(parameter, _targetPropertyName); var constantValue = Expression.Constant(value); var equalsExpr = Expression.Equal(property, constantValue); var lambda = Expression.Lambda(equalsExpr, parameter); var anyMethod = typeof(Queryable).GetMethods() .First(m => m.Name == "Any" && m.GetParameters().Length == 2) .MakeGenericMethod(entityType); var exists = (bool)anyMethod.Invoke(null, new object[] { dbSet, lambda }); return exists ? new ValidationResult(ErrorMessage) : ValidationResult.Success; } }
2. 正确使用Attribute
在模型类中指定DbContext、实体和属性信息:
public class Category { // 传入ApplicationDbContext类型、实体集合名、要校验的属性名 [Unique(typeof(ApplicationDbContext), nameof(Category), nameof(Name))] public string Name { get; set; } public string Description { get; set; } }
3. 确保验证在依赖注入环境中触发
- MVC/API场景:确保控制器标记了
[ApiController]属性,ASP.NET Core会自动在模型绑定后触发验证,并传入完整的ValidationContext。 - 手动验证场景:如果需要手动验证模型,必须手动创建
ValidationContext并传入服务提供者:// 从依赖注入容器获取IServiceProvider var serviceProvider = ...; var category = new Category { Name = "测试分类" }; var validationContext = new ValidationContext(category, serviceProvider, null); var validationResults = new List<ValidationResult>(); var isValid = Validator.TryValidateObject(category, validationContext, validationResults, validateAllProperties: true); - EF Core保存时验证:如果要在
SaveChanges时触发验证,需要在DbContext中手动创建ValidationContext:public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { var serviceProvider = this.GetService<IServiceProvider>(); foreach (var entry in ChangeTracker.Entries() .Where(e => e.State is EntityState.Added or EntityState.Modified)) { var validationContext = new ValidationContext(entry.Entity, serviceProvider, null); Validator.ValidateObject(entry.Entity, validationContext, validateAllProperties: true); } return await base.SaveChangesAsync(cancellationToken); }
内容的提问来源于stack exchange,提问作者Mahesh Kantariya
相关产品推荐
相关产品推荐

