16

我正在尝试将 dapper 与 Oracle(ODP.NET)一起使用,并且我想使用“QueryMultiple”功能。

将此字符串传递给 QueryMultiple 方法:

 var query = "Select CUST_ID CustId from Customer_info WHERE CUST_ID=:custId;" +
                   "Select CUST_ID CustId from BCR WHERE CUST_ID=:custId";

我收到 ORA-00911:无效字符错误

有没有办法做到这一点,或者这是不可能的?

Tks

4

3 回答 3

35

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();
    }
于 2014-07-16T01:16:46.350 回答
2

基于grayseal96 的有用答案,我创建了以下实现IDynamicParameters

public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
    private readonly DynamicParameters dynamicParameters;

    private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();

    public OracleDynamicParameters(params string[] refCursorNames) {
        dynamicParameters = new DynamicParameters();
        AddRefCursorParameters(refCursorNames);
    }

    public OracleDynamicParameters(object template, params string[] refCursorNames) {
        dynamicParameters = new DynamicParameters(template);
        AddRefCursorParameters(refCursorNames);
    }

    private void AddRefCursorParameters(params string[] refCursorNames)
    {
        foreach (string refCursorName in refCursorNames)
        {
            var oracleParameter = new OracleParameter(refCursorName, OracleDbType.RefCursor, ParameterDirection.Output);
            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());
        }
    }
}

假设相同的查询,它可以这样使用:

var queryParams = new { id };
string[] refCursorNames = { "rslt1", "rslt2", "rslt3" };
var dynParams = new OracleDynamicParameters(queryParams, refCursorNames);
...
var multi = dbConn.QueryMultiple(sql, param: dynParams);
于 2016-12-12T22:07:15.480 回答
0

我怀疑这是两三个不同的事情:

  1. 您的第一个查询不应有分号
  2. 查询之间没有换行符
  3. 使用说明暗示绑定字符@不是:(不知道这是否取决于所使用的 RDBMS)。

如果您查看Dapper Google 代码页面,给出的示例QueryMultiple()是:

var sql = 
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";

using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
   var customer = multi.Read<Customer>().Single();
   var orders = multi.Read<Order>().ToList();
   var returns = multi.Read<Return>().ToList();
   ...
} 

去掉分号;添加一个新行,如果仍然有问题,请更改绑定字符。

于 2013-09-12T20:36:18.430 回答