Core 2项目自定义Validation Attribute无法访问DB Context的咨询
自定义Validation Attribute无法访问DB Context的问题排查与修复
问题描述
我正在Core 2项目中创建一个自定义Validation Attribute(原文拼写为valdiation),需要将输入值与数据库中的现有值列表进行校验。但下方代码无法正常运行,无法访问DB Context,请问原因是什么以及如何修正?
用户提供的代码:
public class BibValidatorAttribute : ValidationAttribute { protected override ValidationResult IsValid( object value, ValidationContext validationContext) { RaceEntryViewModel raceEntry = (RaceEntryViewModel)validationContext.ObjectInstance; ApplicationDbContext _context = new ApplicationDbContext(); var dbraceEntry = _context.RaceEntries.FirstOrDefault(c => c.Id == raceEntry.Id); if(raceEntry.BibNumber != dbraceEntry.BibNumber) { if (value != null) { var raceentries = from r in _context.RaceEntries select r; var mycount = raceentries.Count(c => c.BibNumber == raceEntry.BibNumber); if (mycount != 0) { return new ValidationResult("The bib number entered already exists"); } else { return ValidationResult.Success; } } else { return ValidationResult.Success; } } else { return ValidationResult.Success; } } }
问题原因分析
- 直接实例化DbContext的错误做法:你通过
new ApplicationDbContext()手动创建上下文实例,完全绕开了ASP.NET Core的依赖注入系统。在Core项目中,DbContext的配置(比如连接字符串、服务生命周期)都是由DI容器管理的,手动实例化会导致上下文无法正确加载配置,自然无法访问数据库。 - 强类型转换的风险:直接把
validationContext.ObjectInstance强制转为RaceEntryViewModel,如果这个Attribute后续被应用到其他模型类上,会直接抛出InvalidCastException,代码健壮性极差。 - 冗余的数据库操作:先查询
dbraceEntry对比编号,再重新查询统计数量,两次数据库访问完全可以合并成一次,既浪费性能又增加出错概率。
修复方案与代码示例
下面是修正后的代码,我标注了关键改动:
public class BibValidatorAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 1. 通过ValidationContext获取DI容器中的DbContext实例 var context = validationContext.GetService(typeof(ApplicationDbContext)) as ApplicationDbContext; if (context == null) { return new ValidationResult("无法获取数据库上下文"); } // 2. 安全转换模型实例,避免强制转换异常 var raceEntry = validationContext.ObjectInstance as RaceEntryViewModel; if (raceEntry == null) { return new ValidationResult("该验证属性仅适用于RaceEntryViewModel类型"); } // 3. 合并查询逻辑:检查BibNumber是否已存在,排除当前编辑的条目 var bibNumber = value?.ToString(); if (!string.IsNullOrEmpty(bibNumber)) { bool bibExists = context.RaceEntries .Any(r => r.BibNumber == bibNumber && r.Id != raceEntry.Id); if (bibExists) { return new ValidationResult("The bib number entered already exists"); } } return ValidationResult.Success; } }
关键改动说明
- 通过DI获取DbContext:使用
validationContext.GetService()从依赖注入容器中获取已经配置好的ApplicationDbContext,确保上下文能正确连接数据库,同时遵循框架的生命周期管理。 - 安全类型转换:用
as关键字进行转换,并判断转换结果是否为null,避免类型不匹配时的异常。 - 简化查询逻辑:使用
Any()方法直接判断是否存在重复的BibNumber,同时通过r.Id != raceEntry.Id排除当前正在编辑的条目(如果是更新操作的话),一次查询完成校验,性能更优。
内容的提问来源于stack exchange,提问作者Matthew Warr
相关产品推荐
相关产品推荐

