Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Korzystanie z Dapper QueryMultiple w Oracle

OP prawdopodobnie już dawno rozwiązał ten problem, ale w chwili pisania tego pytania to pytanie ma tylko jedną odpowiedź i tak naprawdę nie rozwiązuje problemu używania funkcji QueryMultiple() Dappera metoda z Oracle. Jak poprawnie stwierdza @Kamolas81, używając składni z oficjalnych przykładów, rzeczywiście otrzymamy ORA-00933: SQL command not properly ended Komunikat o błędzie. Spędziłem trochę czasu szukając jakiejś dokumentacji na temat tego, jak wykonać QueryMultiple() z Oracle, ale byłem zaskoczony, że tak naprawdę nie było jednego miejsca, które miałoby odpowiedź. Myślałem, że to dość powszechne zadanie. Pomyślałem, że umieszczę odpowiedź tutaj, aby uratować mnie :) ktoś kiedyś w przyszłości na wypadek, gdyby ktoś miał ten sam problem.

Dapper wydaje się, że po prostu przekazuje polecenie SQL bezpośrednio do ADO.NET i dowolnego dostawcy bazy danych, który wykonuje polecenie. W składni z przykładów, gdzie każde polecenie jest oddzielone łamaniem linii, serwer SQL zinterpretuje to jako wiele zapytań do wykonania względem bazy danych i uruchomi każde z zapytań i zwróci wyniki do osobnych danych wyjściowych. Nie jestem ekspertem od ADO.NET, więc może mylę terminologię, ale efekt końcowy jest taki, że Dapper otrzymuje wiele wyników zapytań, a następnie działa magicznie.

Oracle jednak nie rozpoznaje wielu zapytań; uważa, że ​​polecenie SQL jest zniekształcone i zwraca ORA-00933 wiadomość. Rozwiązaniem jest użycie kursorów i zwrócenie danych wyjściowych w kolekcji DynamicParameters. Na przykład wersja SQL Server wyglądałaby tak:

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

wersja zapytania Oracle musiałaby wyglądać tak:

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

W przypadku zapytań uruchamianych na serwerze SQL Server Dapper może je stamtąd obsłużyć. Jednakże, ponieważ zwracamy zestawy wyników do parametrów kursora, musimy użyć IDynamicParameters kolekcja, aby określić parametry polecenia. Aby dodać dodatkową zmarszczkę, normalna DynamicParameters.Add() Metoda w Dapper używa System.Data.DbType dla opcjonalnego parametru dbType, ale parametry kursora dla zapytania muszą być typu Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor . Aby rozwiązać ten problem, użyłem rozwiązania, które @Daniel Smith zaproponował w tej odpowiedzi i utworzyliśmy niestandardową implementację IDynamicParameters interfejs:

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

Więc cały kod razem wygląda mniej więcej tak:

    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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. czy nieunikalny indeks pozwala uniknąć powielania rekordów?

  2. jak uzyskać czas w milisekundach z pola daty wyroczni dla daty 01-01-9999

  3. Jak zwrócić dzisiejszą datę do zmiennej w Oracle

  4. Używanie aliasu w klauzuli WHERE

  5. Błąd we/wy:przekroczono limit czasu odczytu gniazda. Jakie są powody?