Oracle Entity Framework 核心传递 table 参数给存储过程
Oracle Entity Framework Core pass table parameter to stored procedure
我正在尝试使用 Oracle.EntityFrameworkCore
包将参数传递给存储过程,如下所示:
DataTable table = new DataTable();
table.Columns.Add("keyColumn", typeof(string));
table.Columns.Add("valueColumn", typeof(string));
var row = table.NewRow();
row.ItemArray = new object[]
{
entry.KeyColumn,
entry.ValueColumn
};
table.Rows.Add(row);
var parameter = new OracleParameter("entries",table);
parameter.UdtTypeName = "entry_type_list";
return context.Database.ExecuteSqlCommandAsync(
new RawSqlString( @"EXEC set_entry_list (:entries)" ),
parameter);
存储过程和类型定义如下:
CREATE OR REPLACE TYPE entry_type AS OBJECT
(
"keyColumn" NVARCHAR2(3),
"valueColumn" NVARCHAR2(3)
);
CREATE OR REPLACE TYPE entry_type_list AS TABLE OF entry_type;
CREATE OR REPLACE PROCEDURE set_entry_list (entries entry_type_list) AS
BEGIN
REM Doing stuff
END;
但是我得到一个错误:
System.ArgumentException: Value does not fall within the expected range.
at Oracle.ManagedDataAccess.Client.OracleParameter..ctor(String parameterName, Object obj)
这方面的唯一来源是 如何使用 SQL 服务器执行此操作,但没有针对使用 EFCore 的 Oracle 的答案。这里的问题似乎是 Oracle 只接受 OracleParameter
而其他人使用 SqlParameter
。
如果我像这样使用 SqlParameter
类型:
var parameter = new SqlParameter("entries", SqlDbType.Structured);
parameter.TypeName = "entry_type_list";
parameter.Value = table;
我收到这个错误:
System.InvalidCastException: Unable to cast object of type'System.Data.SqlClient.SqlParameter' to type 'Oracle.ManagedDataAccess.Client.OracleParameter'.
我也试过将 parameter.OracleDbType
设置为不同的值,例如 Blob
、RefCursor
、Clob
或 XmlType
,将 parameter.DbType
设置为Object
或将 CollectionType
设置为 PLSQLAssociativeArray
但没有成功。同时传递一个列表或对象数组而不是 table 也没有成功。
我目前不知道还能尝试什么。
以高效方式将大量实体传递给存储过程的任何方法都会有所帮助。我将它们与合并命令一起使用,因此我需要能够将这些参数转换为 table.
我现在找到了一个使用临时 table 并将这个作为我的输入参数的解决方案。
因为我不能传递一个完整的 table,但是我必须通过为每一列传递一个数组来填充这个 table 的简单对象数组:
var keyColumn = new OracleParameter( "keyColumn", OracleDbType.Decimal );
keyColumn.Value = values.Select( c => c.KeyColumn).ToArray();
var valueColumn = new OracleParameter( "valueColumn", OracleDbType.Decimal );
valueColumn = values.Select( c => c.ValueColumn).ToArray();
using ( var transaction = this.dbContext.Database.BeginTransaction( IsolationLevel.ReadCommitted) )
{
var connection = this.dbContext.Database.GetDbConnection() as OracleConnection;
OracleCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
INSERT INTO TMP_TABLE
(
""keyColumn"",
""valueColumn""
)
VALUES (
:keyColumn,
:valueColumn)";
cmd.Parameters.Add( keyColumn );
cmd.Parameters.Add( valueColumn );
cmd.ArrayBindCount = values.Length;
var insertCount = await cmd.ExecuteNonQueryAsync();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.stored_procedure";
var result = await cmd.ExecuteNonQueryAsync();
transaction.Commit();
}
我这样创建了临时 table:
CREATE
GLOBAL TEMPORARY TABLE "dbo"."TMP_TABLE"
ON COMMIT DELETE ROWS
AS SELECT * FROM "dbo"."REAL_TABLE" WHERE 0=1;
并更改我的存储过程以使用它:
CREATE OR REPLACE PROCEDURE stored_procedure AS
BEGIN
REM use the "dbo"."TMP_TABLE"
END;
This answer 帮助我使用每列一个数组的批量插入方法。该线程还包含有关该主题的一些进一步讨论和更通用的方法。
我正在尝试使用 Oracle.EntityFrameworkCore
包将参数传递给存储过程,如下所示:
DataTable table = new DataTable();
table.Columns.Add("keyColumn", typeof(string));
table.Columns.Add("valueColumn", typeof(string));
var row = table.NewRow();
row.ItemArray = new object[]
{
entry.KeyColumn,
entry.ValueColumn
};
table.Rows.Add(row);
var parameter = new OracleParameter("entries",table);
parameter.UdtTypeName = "entry_type_list";
return context.Database.ExecuteSqlCommandAsync(
new RawSqlString( @"EXEC set_entry_list (:entries)" ),
parameter);
存储过程和类型定义如下:
CREATE OR REPLACE TYPE entry_type AS OBJECT
(
"keyColumn" NVARCHAR2(3),
"valueColumn" NVARCHAR2(3)
);
CREATE OR REPLACE TYPE entry_type_list AS TABLE OF entry_type;
CREATE OR REPLACE PROCEDURE set_entry_list (entries entry_type_list) AS
BEGIN
REM Doing stuff
END;
但是我得到一个错误:
System.ArgumentException: Value does not fall within the expected range.
at Oracle.ManagedDataAccess.Client.OracleParameter..ctor(String parameterName, Object obj)
这方面的唯一来源是 OracleParameter
而其他人使用 SqlParameter
。
如果我像这样使用 SqlParameter
类型:
var parameter = new SqlParameter("entries", SqlDbType.Structured);
parameter.TypeName = "entry_type_list";
parameter.Value = table;
我收到这个错误:
System.InvalidCastException: Unable to cast object of type'System.Data.SqlClient.SqlParameter' to type 'Oracle.ManagedDataAccess.Client.OracleParameter'.
我也试过将 parameter.OracleDbType
设置为不同的值,例如 Blob
、RefCursor
、Clob
或 XmlType
,将 parameter.DbType
设置为Object
或将 CollectionType
设置为 PLSQLAssociativeArray
但没有成功。同时传递一个列表或对象数组而不是 table 也没有成功。
我目前不知道还能尝试什么。
以高效方式将大量实体传递给存储过程的任何方法都会有所帮助。我将它们与合并命令一起使用,因此我需要能够将这些参数转换为 table.
我现在找到了一个使用临时 table 并将这个作为我的输入参数的解决方案。
因为我不能传递一个完整的 table,但是我必须通过为每一列传递一个数组来填充这个 table 的简单对象数组:
var keyColumn = new OracleParameter( "keyColumn", OracleDbType.Decimal );
keyColumn.Value = values.Select( c => c.KeyColumn).ToArray();
var valueColumn = new OracleParameter( "valueColumn", OracleDbType.Decimal );
valueColumn = values.Select( c => c.ValueColumn).ToArray();
using ( var transaction = this.dbContext.Database.BeginTransaction( IsolationLevel.ReadCommitted) )
{
var connection = this.dbContext.Database.GetDbConnection() as OracleConnection;
OracleCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
INSERT INTO TMP_TABLE
(
""keyColumn"",
""valueColumn""
)
VALUES (
:keyColumn,
:valueColumn)";
cmd.Parameters.Add( keyColumn );
cmd.Parameters.Add( valueColumn );
cmd.ArrayBindCount = values.Length;
var insertCount = await cmd.ExecuteNonQueryAsync();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.stored_procedure";
var result = await cmd.ExecuteNonQueryAsync();
transaction.Commit();
}
我这样创建了临时 table:
CREATE
GLOBAL TEMPORARY TABLE "dbo"."TMP_TABLE"
ON COMMIT DELETE ROWS
AS SELECT * FROM "dbo"."REAL_TABLE" WHERE 0=1;
并更改我的存储过程以使用它:
CREATE OR REPLACE PROCEDURE stored_procedure AS
BEGIN
REM use the "dbo"."TMP_TABLE"
END;
This answer 帮助我使用每列一个数组的批量插入方法。该线程还包含有关该主题的一些进一步讨论和更通用的方法。