Oracle
 sql >> Base de données >  >> RDS >> Oracle

Utilisation de Dapper QueryMultiple dans Oracle

L'OP a probablement résolu le problème depuis longtemps, mais au moment de la rédaction, cette question n'a qu'une seule réponse et ne résout pas vraiment le problème de l'utilisation de QueryMultiple() de Dapper. méthode avec Oracle. Comme @ Kamolas81 l'indique correctement, en utilisant la syntaxe des exemples officiels, on obtiendra en effet le ORA-00933: SQL command not properly ended Message d'erreur. J'ai passé un moment à chercher une sorte de documentation sur la façon de faire QueryMultiple() avec Oracle, mais j'ai été surpris qu'il n'y ait pas vraiment un endroit qui ait une réponse. J'aurais pensé que c'était une tâche assez courante. J'ai pensé que je posterais une réponse ici pour me sauver :) quelqu'un dans le futur juste au cas où quelqu'un aurait le même problème.

Dapper semble simplement transmettre la commande SQL directement à ADO.NET et au fournisseur de base de données qui exécute la commande. Dans la syntaxe des exemples, où chaque commande est séparée par un saut de ligne, le serveur SQL interprétera cela comme plusieurs requêtes à exécuter sur la base de données et il exécutera chacune des requêtes et renverra les résultats dans des sorties séparées. Je ne suis pas un expert ADO.NET, donc je confonds peut-être la terminologie, mais l'effet final est que Dapper obtient les multiples résultats de la requête, puis opère sa magie.

Oracle, cependant, ne reconnaît pas les multiples requêtes; il pense que la commande SQL est malformée et renvoie le ORA-00933 message. La solution consiste à utiliser des curseurs et à renvoyer la sortie dans une collection DynamicParameters. Par exemple, alors que la version de SQL Server ressemblerait à ceci :

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

la version Oracle de la requête devrait ressembler à ceci :

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;";

Pour les requêtes exécutées sur SQL Server, Dapper peut les gérer à partir de là. Cependant, comme nous renvoyons les jeux de résultats dans les paramètres du curseur, nous devrons utiliser un IDynamicParameters collection pour spécifier les paramètres de la commande. Pour ajouter une ride supplémentaire, le DynamicParameters.Add() normal dans Dapper utilise un System.Data.DbType pour le paramètre facultatif dbType, mais les paramètres de curseur pour la requête doivent être de type Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor . Pour résoudre ce problème, j'ai utilisé la solution proposée par @Daniel Smith dans cette réponse et créé une implémentation personnalisée 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());
            }
        }
    }

Ainsi, tout le code ressemble à ceci :

    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();
    }