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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:57:28