反射调用MethodInfo时遇‘对象与目标类型不匹配’错误求助
问题分析与解决方案
错误原因拆解
你遇到的Reflection method invoke object does not match target type错误,核心是调用实例方法时传错了目标对象:
你写了method.Invoke(this, params),这里的this是CheckUnique验证属性的实例,但IsValid方法属于CustomRepository<T>的实例——必须用从DI容器拿到的CustomRepository<T>实例来调用这个方法,而不是当前的验证属性对象。
修正后的动态实现方案
我们可以把验证属性改成支持动态指定实体类型和属性名的版本,彻底摆脱硬编码,同时修复反射调用的错误:
1. 重构CheckUnique验证属性
public class CheckUnique : ValidationAttribute { // 新增可配置属性,指定要验证的实体类型和属性名 public Type EntityType { get; set; } public string PropertyName { get; set; } // 静态缓存MethodInfo,避免每次验证重复反射,提升性能 private static readonly Dictionary<Type, MethodInfo> _methodCache = new Dictionary<Type, MethodInfo>(); protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 校验配置参数是否完整 if (EntityType == null || string.IsNullOrEmpty(PropertyName)) { return new ValidationResult("请为CheckUnique特性指定EntityType和PropertyName"); } // 动态构建CustomRepository<T>的泛型类型 Type genericRepoType = typeof(CustomRepository<>).MakeGenericType(EntityType); // 从DI容器获取对应的Repository实例 var repoInstance = validationContext.GetService(genericRepoType); if (repoInstance == null) { return new ValidationResult("无法从DI容器获取对应的CustomRepository实例"); } // 从缓存获取或反射获取IsValid方法 if (!_methodCache.TryGetValue(genericRepoType, out MethodInfo isValidMethod)) { isValidMethod = genericRepoType.GetMethod("IsValid", new[] { typeof(object), typeof(string) }); if (isValidMethod == null) { return new ValidationResult("CustomRepository<T>中未找到匹配签名的IsValid方法"); } _methodCache[genericRepoType] = isValidMethod; } // 调用方法:注意第一个参数是repoInstance(CustomRepository实例),不是this bool isValid = (bool)isValidMethod.Invoke(repoInstance, new object[] { value, PropertyName }); if (!isValid) { return new ValidationResult($"{PropertyName}已存在", new[] { this.ErrorMessageName }); } return ValidationResult.Success; } }
2. 实际使用示例
在你的Model中,只需通过特性参数指定要验证的实体和属性即可,无需重复编写验证逻辑:
public class RegisterModel { [CheckUnique(EntityType = typeof(ApplicationUser), PropertyName = "username", ErrorMessage = "用户名已被占用")] public string Username { get; set; } [CheckUnique(EntityType = typeof(ApplicationUser), PropertyName = "email", ErrorMessage = "邮箱已被注册")] public string Email { get; set; } }
关键注意点
- 实例方法的
Invoke第一个参数必须是该方法所属类的实例,绝对不能传当前验证属性的this - 通过
typeof(CustomRepository<>).MakeGenericType(EntityType)动态构建泛型类型的逻辑是正确的,只需确保DI容器中已正确注册CustomRepository<>(你Startup里的services.AddScoped(typeof(CustomRepository<>));没问题) - 加入MethodInfo缓存可以避免每次验证都做反射操作,提升高并发场景下的性能
内容的提问来源于stack exchange,提问作者GlaMin
相关产品推荐
相关产品推荐

