如何使用类型变量查询 Dapper

how to query with Dapper with a type variable

这是我的设置

首先,基础classes

public class TableBase
{
}

public class TableRepositoryBase
{
    private BindingSource _bindingSourceOnForm;

    public TableRepositoryBase(BindingSource bindingSource, string connectionString)
    {
        BindingSourceOnForm = bindingSource;
        ConnectionString = connectionString;
        SetupParameters();
    }

    protected string ConnectionString { get; set; }

    protected Type TableType { 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();
            BindingSourceOnForm.DataSource = connection.Query<TableType>(SQLSelect).ToList();
        }
    }
}

现在我继承了 classes

public class TableUser : TableBase
{
    public int UserID { get; set; }
    public string UserFirst { get; set; }
    public string UserLastname { get; set; }
}

public class TableRepositoryUser : TableRepositoryBase
{
    public TableRepositoryUser(BindingSource bindingSource, string connectionString) : base(bindingSource, connectionString)
    { }

    protected override void SetupParameters()
    {
        base.SetupParameters();

        TableType = typeof(TableUser);
        SQLSelect = "select UserID, UserFirst, UserLastname from tblUser";
    }
}

现在的问题
在基础 class TableRepositoryBase 方法 FetchAll() 中的以下代码行将无法编译:

BindingSourceOnForm.DataSource = connection.Query<TableType>(SQLSelect).ToList();

错误是

Error CS0246 The type or namespace name 'TableType' could not be found (are you missing a using directive or an assembly reference?)

这是因为 query<> 正在寻找类型,虽然 TableType 被声明为类型,但它可能会在这里将其视为变量。

我想实现的是方法FetchAll可以完全由baseclass完成。
但是,我希望放在 BindingSourceOnForm.DataSource 中的列表不是 DapperRow 类型,但在这种情况下是 TableUser.

类型

我可以像这样在 TableRepositoryUser 中覆盖此方法

public override void FetchAll()
{
    //base.FetchAll();
    
    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        connection.Open();
        BindingSourceOnForm.DataSource = connection.Query<TableUser>(SQLSelect).ToList();
    }
}

这会起作用,现在分配给 BindingSourceOnForm.DataSource 的列表如果类型为 TableUser
但我想在基础 class 中执行此代码,而不是在继承的 class.
中执行此代码 我怎样才能做到这一点?

编辑
我正在尝试调整来自 @Flydog57 评论中的 link 的答案,但没有成功。
到目前为止我有这个

MethodInfo method = typeof(SqlConnection).GetMethod("Query");
MethodInfo generic = method.MakeGenericMethod(TableType);

BindingSourceOnForm.DataSource = generic.(SQLSelect).ToList();

我遇到编译错误Identifier expected
我想这段代码根本没有意义,但我不明白如何使用 MakeGenericMethod 来解决我的问题,如果可能的话。

非常感谢您的帮助

编辑 2

由于找不到解决方法,我现在使用这种低效的方法

public virtual void FetchAll()
{
    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        connection.Open();

        if (TableType == typeof(TableUser))
            BindingSourceOnForm.DataSource = connection.Query<TableUser>(SQLSelect).ToList();
        else if (TableType == typeof(TableTask))
            BindingSourceOnForm.DataSource = connection.Query<TableTask>(SQLSelect).ToList();
    }
}

仍然希望有更好的方法,因为这意味着每次从 RepositoryBase

继承新的存储库 class 时,我都必须回到这里修改代码

我假设你正在尝试做的是翻译这个:

public virtual void FetchAll()
{
    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        connection.Open();
        BindingSourceOnForm.DataSource = connection.Query<TableType>(SQLSelect).ToList();
    }
}

进入功能代码,调用 connection.Query 使用 TableType 表示的类型作为 Query 函数的通用参数。

我没有使用您的抽象代码,而是使用了 SQL Pubs 数据库样本(我的笔记本电脑上有它)和我拼凑的 Authors class。所以我要翻译的代码看起来像这样:

public string ConnectionString { get; set; } = @"Data Source=[MyLaptopName];Trusted_Connection=true";
public string SelectString { get; set; } = @"SELECT TOP (1000) [au_id], [au_lname], [au_fname],[phone],[address],[city],[state],[zip],[contract]FROM [pubs].[dbo].[authors]";

作者 class 看起来像:

public class Authors
{
    public string au_id { get; set; }
    public string au_lname { get; set; }
    public string au_fname { get; set; }
    public string phone { get; set; }
    public string address { get; set; }
    public string city { get; set; }
    public string state { get; set; }
    public string zip { get; set; }
    public string contract { get; set; }
}

我可以用这个极简代码阅读 table 的内容:

using (var conn = new SqlConnection(ConnectionString))
{
    var result = conn.Query<Authors>(SelectString);
}

因此,以 How do I use reflection to call a generic method? 为指导,第一个障碍是如何获取对表示调用 Query 的 MethodInfo 的引用。

这是 SqlConnection 上的扩展方法,在 Dapper.SqlMapper class 上作为静态方法实现。它是通用的并且只有一个类型参数。我们要调用的特定覆盖有七个参数(Dapper.SqlMapper 中定义了 11 个查询方法 - 我们需要选择正确的一个)。

我正在以这种方式(相当蛮力)获取方法:

private MethodInfo GetDapperQueryMethod()
{
    var dapperMapperType = typeof(Dapper.SqlMapper);
    //get the first method named "Query" that has a single generic type parameter and seven regular parameters
    var queryMethods = dapperMapperType.GetMethods(BindingFlags.Public | BindingFlags.Static)
        .Where(m => m.Name == "Query" && m.IsGenericMethod && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 7);
    return queryMethods.FirstOrDefault();
}

您可能想查看此内容以了解其他执行此操作的方法:Reflection to Identify Extension Methods

一旦我们有了代表 Query 的正确覆盖的 MethodInfo,剩下的就相当简单了。首先,您需要构造泛型方法,将泛型方法定义与泛型参数类型结合起来(在您的情况下为 TableType,在我的情况下为 Authors)。您使用 MakeGenericMethod.

创建 MethodInfo

您需要调用该方法 - 但作为一个简单的静态方法(反射通常不关心扩展方法)。所以,第一个参数是连接。最后的参数是每个参数的默认值(在连接和查询字符串之后)。所以:

var method = GetDapperQueryMethod();
var authorsType = typeof(Authors);
var genericQuery = method.MakeGenericMethod(authorsType);
using (var conn = new SqlConnection(ConnectionString))
{
    var result = genericQuery.Invoke(null, new object[] {conn, SelectString, null, null, true, null, null});
}