如何在Dapper中实现基类通用FetchAll方法,避免子类重复重写?
解决方法一:正确使用反射调用Dapper的Query泛型方法
你之前的反射代码有两个关键错误:
- Dapper的
Query是扩展方法,定义在SqlMapper类中,不是SqlConnection的实例方法,所以不能从typeof(SqlConnection)获取这个方法。 - 调用泛型方法实例时,需要用
Invoke方法来执行,而不是你写的generic.(SQLSelect)这种错误语法。
修正后的FetchAll方法代码如下:
public virtual void FetchAll() { using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); // 获取Dapper Query<T>的正确重载:接收SqlConnection和string参数的扩展方法 MethodInfo queryMethod = typeof(SqlMapper).GetMethods() .Where(m => m.Name == "Query" && m.IsGenericMethod) .Select(m => new { Method = m, Params = m.GetParameters() }) .Where(x => x.Params.Length == 2 && x.Params[0].ParameterType == typeof(SqlConnection) && x.Params[1].ParameterType == typeof(string)) .Select(x => x.Method) .FirstOrDefault(); if (queryMethod == null) { throw new InvalidOperationException("无法找到Dapper的Query方法重载"); } // 创建泛型方法实例,指定TableType为泛型参数 MethodInfo genericQueryMethod = queryMethod.MakeGenericMethod(TableType); // 调用泛型方法:第一个参数是扩展方法的this对象(即connection),后续是方法参数 var queryResult = genericQueryMethod.Invoke(null, new object[] { connection, SQLSelect }); // 将结果转换为List并绑定到BindingSource var resultList = ((IEnumerable)queryResult).Cast<object>().ToList(); BindingSourceOnForm.DataSource = resultList; } }
解决方法二:改用泛型基类(更优雅、类型安全)
反射方案虽然能解决问题,但代码繁琐且缺乏类型安全。更推荐的方式是将基类改为泛型基类,这样可以直接使用Dapper的泛型Query方法,无需反射:
重构后的基类定义
public class TableBase { } public class TableRepositoryBase<T> where T : TableBase { private BindingSource _bindingSourceOnForm; public TableRepositoryBase(BindingSource bindingSource, string connectionString) { BindingSourceOnForm = bindingSource; ConnectionString = connectionString; SetupParameters(); } protected string ConnectionString { get; set; } protected BindingSource BindingSourceOnForm { get { return _bindingSourceOnForm; } set { _bindingSourceOnForm = value;} } protected string SQLSelect { get; set; } protected virtual void SetupParameters() { } public virtual void FetchAll() { using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); // 直接使用泛型参数T,类型安全且无需反射 BindingSourceOnForm.DataSource = connection.Query<T>(SQLSelect).ToList(); } } }
重构后的继承类定义
public class TableUser : TableBase { public int UserID { get; set; } public string UserFirst { get; set; } public string UserLastname { get; set; } } public class TableRepositoryUser : TableRepositoryBase<TableUser> { public TableRepositoryUser(BindingSource bindingSource, string connectionString) : base(bindingSource, connectionString) { } protected override void SetupParameters() { base.SetupParameters(); SQLSelect = "select UserID, UserFirst, UserLastname from tblUser"; } }
这个方案的优势:
- 完全避免反射,代码简洁易读
- 类型安全,编译时就能发现错误
- 子类无需重写
FetchAll,只需要实现SetupParameters设置SQL语句即可,扩展性更好
为什么原来的代码编译失败?
泛型参数需要的是编译时已知的类型,而TableType是一个Type类型的变量,属于运行时信息,无法直接作为泛型参数传递。这就是你遇到CS0246错误的核心原因。
内容的提问来源于stack exchange,提问作者GuidoG
相关产品推荐
相关产品推荐

