sql >> Databasteknik >  >> RDS >> Oracle

Använder Dapper QueryMultiple i Oracle

OP har förmodligen för länge sedan löst problemet vid det här laget, men i skrivande stund har denna fråga bara ett svar och den löser inte riktigt problemet med att använda Dappers QueryMultiple() metod med Oracle. Som @Kamolas81 korrekt anger, genom att använda syntaxen från de officiella exemplen, kommer man verkligen att få ORA-00933: SQL command not properly ended felmeddelande. Jag letade ett tag efter någon sorts dokumentation om hur man gör QueryMultiple() med Oracle, men jag blev förvånad över att det inte riktigt fanns ett ställe som hade ett svar. Jag skulle ha trott att detta var en ganska vanlig uppgift. Jag tänkte lägga upp ett svar här för att rädda mig :) någon någon gång i framtiden ifall någon råkar ha samma problem.

Dapper verkar bara skicka SQL-kommandot direkt till ADO.NET och vilken db-leverantör som än kör kommandot. I syntaxen från exemplen, där varje kommando separeras av en radbrytning, kommer SQL-servern att tolka det som flera frågor som ska köras mot databasen och den kommer att köra var och en av frågorna och returnera resultaten till separata utdata. Jag är inte en ADO.NET-expert, så jag kan förstöra terminologin, men sluteffekten är att Dapper får flera frågeutmatningar och sedan gör sin magi.

Oracle känner dock inte igen de flera frågorna; den tror att SQL-kommandot är felaktigt format och returnerar ORA-00933 meddelande. Lösningen är att använda markörer och returnera utdata i en DynamicParameters-samling. Till exempel, medan SQL Server-versionen skulle se ut så här:

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

Oracle-versionen av frågan skulle behöva se ut så här:

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

För frågor som körs mot SQL Server kan Dapper hantera det därifrån. Men eftersom vi returnerar resultatuppsättningarna till markörparametrar, måste vi använda en IDynamicParameters samling för att ange parametrar för kommandot. För att lägga till en extra rynka, den normala DynamicParameters.Add() metod i Dapper använder en System.Data.DbType för den valfria parametern dbType, men markörparametrarna för frågan måste vara av typen Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor . För att lösa detta använde jag lösningen som @Daniel Smith föreslog i det här svaret och skapade en anpassad implementering av IDynamicParameters gränssnitt:

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

Så all kod tillsammans blir ungefär så här:

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


  1. Vilken MySQL-datatyp som ska användas för att lagra booleska värden

  2. konstig teckenkodning av lagrad data, gammalt skript visar dem bra, nytt gör det inte

  3. Hur man konfigurerar MySQL för att vara skiftlägeskänslig

  4. Hur kan jag infoga identitet manuellt?