我有一个异步方法,它使用 dapper 返回动态类型,但在调用方法中我想要一个具体的类。例如...
public async Task<dynamic> MyDapperMethod(string sql)
{
var result = await conn.QueryFirstOrDefaultAsync(sql);
return result;
}
然后在调用方法中...
public async Task<Customer> GetCustomer(int id)
{
Customer cust = new Customer();
var qry = await MyDapperMethod($"SELECT * FROM Customers WHERE Id = {id}");
cust = (Customer)qry;
return cust;
}
我收到无效的转换错误。它无法将“DapperRow”强制转换为“Customer”。 有什么办法可以解决这个问题吗?
一些建议是手动将 DapperRow 中的每个字段移动到具体类中相应的属性。但还有更好的办法吗?
改用 QueryFirstOrDefaultAsync<>()。
public async Task<Customer> MyDapperMethod(string sql)
{
var result = await conn.QueryFirstOrDefaultAsync<Customer>(sql);
return result;
}
最好的答案是“不要使用
dynamic
”。但是,我意识到有时您最终不得不使用一些库代码。如果您不打算使用动态,您可以将您的MyDapperMethod
更改为类似于@sarmad建议的内容,例如:
public async Task<T> MyDapperMethod(string sql)
{
var result = await conn.QueryFirstOrDefaultAsync<T>(sql);
return result;
}
您可以将其称为传递您的
Customer
类型。
但是,您会注意到该方法仅抽象您的连接处理。否则,它看起来非常像原始的 Dapper 调用。
一些代码审查:
当我们这样做时,您可能以错误的方式进行连接处理。
SqlConnection
类型(与大多数 IDbConnection
实现一样)期望消费者为每个工作单元创建一个新连接,然后快速处置该新连接 - 它会在幕后为您进行池化。
因此,如果您已习惯使用
dynamic
,这里有两种可能的解决方案:
1。使用带有
dynamic
参数 的构造函数
在
Customer
类中创建一个构造函数,如下所示:
public Customer (dynamic customerRecord){
Name = customerRecord.Name;
Address = customerRecord.Address;
PhoneNumber = customerRecord.PhoneNumber;
// etc.
}
有了这个,你的
GetCustomer
最终看起来像:
public async Task<Customer> GetCustomer(int id)
{
var result = await MyDapperMethod($"SELECT * FROM Customers WHERE Id = {id}");
return new Customer (result);
}
更多代码审查
声明
cust
变量时,无需对其进行初始化。通过在代码中执行的操作,您不必要地运行了默认的 Customer
构造函数,并且创建了需要清理的垃圾。另外,不要使用 SQL 查询进行字符串连接或插值(查找“SQL 注入”和“Little Bobby 表”)。您的代码应如下所示:
public async Task<Customer> GetCustomer(int id)
{
var result = await MyDapperMethod($"SELECT * FROM Customers WHERE Id = @Id", new {Id = id});
return new Customer (result);
}
我现在要发布这个。基于反射的解决方案涉及更多 - 我需要完成一些工作。我稍后应该可以更新。