Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Usando Dapper QueryMultiple no Oracle


O OP provavelmente já resolveu o problema há muito tempo, mas no momento em que escrevo, essa pergunta tem apenas uma resposta e realmente não resolve o problema de usar o QueryMultiple() do Dapper método com Oracle. Como @Kamolas81 afirma corretamente, usando a sintaxe dos exemplos oficiais, de fato obteremos o ORA-00933: SQL command not properly ended mensagem de erro. Passei um tempo procurando algum tipo de documentação sobre como fazer QueryMultiple() com a Oracle, mas fiquei surpreso que não havia realmente um lugar que tivesse uma resposta. Eu teria pensado que esta seria uma tarefa bastante comum. Pensei em postar uma resposta aqui para me salvar me :) alguém em algum momento no futuro, caso alguém tenha esse mesmo problema.

Dapper parece apenas passar o comando SQL diretamente para o ADO.NET e qualquer provedor de banco de dados que esteja executando o comando. Na sintaxe dos exemplos, onde cada comando é separado por uma quebra de linha, o SQL Server interpretará isso como várias consultas a serem executadas no banco de dados e executará cada uma das consultas e retornará os resultados em saídas separadas. Não sou um especialista em ADO.NET, então posso estar confundindo a terminologia, mas o efeito final é que o Dapper obtém as várias saídas de consulta e, em seguida, faz sua mágica.

A Oracle, porém, não reconhece as várias consultas; ele acha que o comando SQL está malformado e retorna o ORA-00933 mensagem. A solução é usar cursores e retornar a saída em uma coleção DynamicParameters. Por exemplo, enquanto a versão do SQL Server ficaria assim:
var sql = 
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";

a versão Oracle da consulta precisaria ser assim:
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;";

Para consultas executadas no SQL Server, o Dapper pode lidar com isso. No entanto, como estamos retornando os conjuntos de resultados em parâmetros de cursor, precisaremos usar um IDynamicParameters coleção para especificar parâmetros para o comando. Para adicionar uma ruga extra, o DynamicParameters.Add() normal O método no Dapper usa um System.Data.DbType para o parâmetro opcional dbType, mas os parâmetros do cursor para a consulta precisam ser do tipo Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor . Para resolver isso, usei a solução que @Daniel Smith propôs em esta resposta e criou uma implementação personalizada de IDynamicParameters interface:
    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());
            }
        }
    }

Então todo o código junto fica assim:
    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();
    }