Operační program již pravděpodobně tento problém dávno vyřešil, ale v době psaní tohoto článku má tato otázka pouze jednu odpověď a ve skutečnosti neřeší problém použití Dapper's QueryMultiple()
metoda s Oracle. Jak @Kamolas81 správně uvádí, použitím syntaxe z oficiálních příkladů skutečně získáme ORA-00933: SQL command not properly ended
chybové hlášení. Strávil jsem chvíli hledáním nějaké dokumentace o tom, jak udělat QueryMultiple()
s Oracle, ale překvapilo mě, že ve skutečnosti neexistuje jediné místo, které by mělo odpověď. Myslel bych, že je to docela běžný úkol. Myslel jsem, že zde zveřejním odpověď, abych zachránil me :) někdo někdy v budoucnu pro případ, že by měl někdo stejný problém.
Zdá se, že Dapper pouze předává příkaz SQL přímo do ADO.NET a jakéhokoli poskytovatele db, který příkaz provádí. V syntaxi z příkladů, kde je každý příkaz oddělen zalomením řádku, to SQL server bude interpretovat jako více dotazů, které se mají spustit proti databázi, a spustí každý z dotazů a vrátí výsledky do samostatných výstupů. Nejsem odborník na ADO.NET, takže možná pletu terminologii, ale konečným efektem je, že Dapper získá více výstupů dotazů a poté funguje kouzla.
Oracle však nerozpozná vícenásobné dotazy; domnívá se, že příkaz SQL má nesprávný formát a vrací ORA-00933
zpráva. Řešením je použít kurzory a vrátit výstup v kolekci DynamicParameters. Zatímco verze SQL Server by například vypadala takto:
var sql =
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";
verze dotazu Oracle by měla vypadat takto:
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;";
U dotazů spuštěných proti SQL Server to Dapper může zpracovat odtud. Protože však vracíme sady výsledků do parametrů kurzoru, budeme muset použít IDynamicParameters
kolekce k zadání parametrů příkazu. Chcete-li přidat další vrásky, normální DynamicParameters.Add()
metoda v Dapper používá System.Data.DbType pro volitelný parametr dbType, ale parametry kurzoru pro dotaz musí být typu Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor
. K vyřešení tohoto problému jsem použil řešení, které @Daniel Smith navrhl v této odpovědi
a vytvořili vlastní implementaci IDynamicParameters
rozhraní:
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());
}
}
}
Takže celý kód dohromady vypadá nějak takto:
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();
}