OP 到现在可能早就解决了这个问题,但在撰写本文时,这个问题只有一个答案,它并没有真正解决在QueryMultiple()
Oracle 中使用 Dapper 的方法的问题。正如@Kamolas81 正确指出的那样,通过使用官方示例中的语法,确实会收到ORA-00933: SQL command not properly ended
错误消息。我花了一段时间搜索有关如何QueryMultiple()
使用 Oracle 的某种文档,但令我惊讶的是,实际上没有一个地方有答案。我会认为这是一个相当普遍的任务。我想我会在这里发布一个答案来拯救我:) 将来某个时候有人,以防万一有人碰巧遇到同样的问题。
Dapper 似乎只是将 SQL 命令直接传递给 ADO.NET 以及正在执行该命令的任何 db 提供程序。在示例的语法中,每个命令由换行符分隔,SQL Server 会将其解释为针对数据库运行的多个查询,它将运行每个查询并将结果返回到单独的输出中。我不是 ADO.NET 专家,所以我可能会弄乱术语,但最终效果是 Dapper 获得了多个查询输出,然后发挥了它的魔力。
但是,Oracle 不能识别多个查询。它认为 SQL 命令格式错误并返回ORA-00933
消息。解决方案是使用游标并在 DynamicParameters 集合中返回输出。例如,SQL Server 版本看起来像这样:
var sql =
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";
查询的 Oracle 版本需要如下所示:
var sql = "BEGIN OPEN :rslt1 FOR SELECT * FROM customers WHERE customerid = :id; " +
"OPEN :rslt2 FOR SELECT * FROM orders WHERE customerid = :id; " +
"OPEN :rslt3 FOR SELECT * FROM returns Where customerid = :id; " +
"END;";
对于针对 SQL Server 运行的查询,Dapper 可以从那里处理它。但是,因为我们将结果集返回到游标参数中,所以我们需要使用一个IDynamicParameters
集合来指定命令的参数。为了增加额外的皱纹,DynamicParameters.Add()
Dapper 中的普通方法使用 System.Data.DbType 作为可选的 dbType 参数,但查询的游标参数需要是 type Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor
。为了解决这个问题,我使用了@Daniel Smith 在这个答案中提出的解决方案,并创建了IDynamicParameters
接口的自定义实现:
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Data;
public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
private readonly DynamicParameters dynamicParameters = new DynamicParameters();
private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
{
OracleParameter oracleParameter;
if (size.HasValue)
{
oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
}
else
{
oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
}
oracleParameters.Add(oracleParameter);
}
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
{
var oracleParameter = new OracleParameter(name, oracleDbType, direction);
oracleParameters.Add(oracleParameter);
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
var oracleCommand = command as OracleCommand;
if (oracleCommand != null)
{
oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
}
}
}
所以所有的代码加在一起是这样的:
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Data;
int selectedId = 1;
var sql = "BEGIN OPEN :rslt1 FOR SELECT * FROM customers WHERE customerid = :id; " +
"OPEN :rslt2 FOR SELECT * FROM orders WHERE customerid = :id; " +
"OPEN :rslt3 FOR SELECT * FROM returns Where customerid = :id; " +
"END;";
OracleDynamicParameters dynParams = new OracleDynamicParameters();
dynParams.Add(":rslt1", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":rslt2", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":rslt3", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":id", OracleDbType.Int32, ParameterDirection.Input, selectedId);
using (IDbConnection dbConn = new OracleConnection("<conn string here>"))
{
dbConn.Open();
var multi = dbConn.QueryMultiple(sql, param: dynParams);
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
dbConn.Close();
}