如何使用类型变量查询 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});
}
这是我的设置
首先,基础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
.
您需要调用该方法 - 但作为一个简单的静态方法(反射通常不关心扩展方法)。所以,第一个参数是连接。最后的参数是每个参数的默认值(在连接和查询字符串之后)。所以:
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});
}