Оперативната програма вероятно отдавна е решила проблема досега, но към момента на писане този въпрос има само един отговор и всъщност не решава проблема с използването на QueryMultiple()
на Dapper метод с Oracle. Както @Kamolas81 правилно заявява, като се използва синтаксисът от официалните примери, човек наистина ще получи ORA-00933: SQL command not properly ended
съобщение за грешка. Прекарах известно време в търсене на някакъв вид документация за това как да направя QueryMultiple()
с Oracle, но бях изненадан, че няма наистина едно място, което да има отговор. Бих си помислил, че това е доста често срещана задача. Мислех, че ще публикувам отговор тук, за да ме спаси :) някой в бъдеще, в случай че някой случайно има същия проблем.
Dapper изглежда просто предава SQL командата направо към ADO.NET и който и да е db доставчик изпълнява командата. В синтаксиса от примерите, където всяка команда е разделена с прекъсване на ред, SQL сървърът ще интерпретира това като множество заявки за изпълнение към базата данни и ще изпълни всяка от заявките и ще върне резултатите в отделни изходи. Не съм експерт по ADO.NET, така че може да бъркам терминологията, но крайният ефект е, че Dapper получава множество изходни данни на заявката и след това прави своята магия.
Oracle обаче не разпознава множеството заявки; той смята, че SQL командата е неправилно форматирана и връща ORA-00933
съобщение. Решението е да използвате курсори и да върнете изхода в колекция DynamicParameters. Например, докато версията на SQL Server ще изглежда така:
var sql =
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";
Oracle версията на заявката трябва да изглежда така:
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;";
За заявки, изпълнявани срещу SQL Server, Dapper може да се справи от там. Тъй като обаче връщаме резултатите в параметри на курсора, ще трябва да използваме IDynamicParameters
колекция за указване на параметри за командата. За да добавите допълнителна бръчка, нормалният DynamicParameters.Add()
метод в Dapper използва System.Data.DbType за незадължителния параметър dbType, но параметрите на курсора за заявката трябва да са от тип Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor
. За да реша това, използвах решението, което @Daniel Smith предложи в този отговор
и създаде персонализирана реализация на IDynamicParameters
интерфейс:
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());
}
}
}
Така че целият код заедно върви по следния начин:
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();
}