【问题标题】:how to query with Dapper with a type variable如何使用类型变量使用 Dapper 进行查询
【发布时间】:2021-08-26 12:31:00
【问题描述】:

这是我的设置

首先,基类

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();
        }
    }
}

现在是我继承的类

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";
    }
}

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

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

错误是

错误 CS0246 找不到类型或命名空间名称“TableType” (您是否缺少 using 指令或程序集引用?)

这将是因为query&lt;&gt; 正在寻找一个类型,虽然TableType 被声明为一个类型,但它可能会在这里将其视为一个变量。

我想要实现的是方法FetchAll可以完全由基类完成。
但是,我希望放在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
但我想在基类中执行此代码,而不是在继承类中。
我怎样才能做到这一点?

编辑
我正在尝试调整我从@Flydog57 在 cmets 中获得的链接中的答案,但没有成功。
到目前为止我有这个

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 继承新的存储库类时,我都必须回到这里并更改代码

【问题讨论】:

  • TableTypeSystem.Type 类型的变量,而不是类型。您不能直接使用变量调用泛型函数,只能使用类型。您必须使用反射来执行此操作 (MakeGenericFunction)。看这里:stackoverflow.com/questions/232535/…
  • @Flydog57 感谢您提供此链接。但是,我无法从该问题的答案中为我的案例提取答案,这将解决我的问题。也许你可以写一个关于如何做到这一点的小样本,以便我可以在我的情况下使用它?
  • Dapper 可以使用dynamic 变量进行查询。也许这会对你有所帮助。但是,在某些时候,我会在您的未来看到工厂方法,除非您可以将您的表单绑定到 List&lt;dynamic&gt;。值得一提的是,将转换放在派生类中几乎就是运行时多态性的全部意义所在。
  • @RobertHarvey 实际上你是对的。我应该在派生类中重写这个方法,让多态性完成它的工作。我想我在这里做了一些过度思考。

标签: c# winforms oop inheritance dapper


【解决方案1】:

我假设你想要做的是翻译这个:

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

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

我没有使用您的抽象代码,而是使用 SQL Pubs 数据库示例(我的笔记本电脑上有它)和一个 Authors 类我放在一起。所以我要翻译的代码是这样的:

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]";

使用如下所示的 Authors 类:

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; }
}

我可以用这个极简代码读取该表的内容:

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

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

这是SqlConnection 上的扩展方法,在Dapper.SqlMapper 类上实现为静态方法。它是通用的,并且只有一个类型参数。我们要调用的特定覆盖有七个参数(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});
}

【讨论】:

  • 我忘了说。在我展示的最后一段代码中,result 的静态类型是object(因为这是Invoke 返回的内容)。如果需要,您可以将其转换为 IEnumerable(Author)。但是,当您使用 TableType 变量时,您不能这样做。底层类型是该类型的集合,但由于 IEnumerable&lt;T&gt; 的逆变性,您可能需要仰起头才能以您想要的形式获得它。
猜你喜欢
  • 2019-03-15
  • 2020-05-03
  • 1970-01-01
  • 2020-07-25
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多