【问题标题】:Oracle Entity Framework Core pass table parameter to stored procedureOracle Entity Framework Core 将表参数传递给存储过程
【发布时间】:2020-03-05 07:14:08
【问题描述】:

我正在尝试使用 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:值不在预期范围内。
在 Oracle.ManagedDataAccess.Client.OracleParameter..ctor(String parameterName, Object obj)

对此的唯一来源是an answer 如何使用 SQL Server 执行此操作,但对于使用 EFCore 的 Oracle 没有答案。这里的问题似乎是 Oracle 只接受 OracleParameter 而其他人使用 SqlParameter

如果我像这样使用SqlParameter 类型:

var parameter = new SqlParameter("entries", SqlDbType.Structured);
parameter.TypeName = "entry_type_list";
parameter.Value = table;

我收到此错误:

System.InvalidCastException:无法将“System.Data.SqlClient.SqlParameter”类型的对象转换为“Oracle.ManagedDataAccess.Client.OracleParameter”类型。

我也尝试将parameter.OracleDbType 设置为不同的值,例如BlobRefCursorClobXmlType,将parameter.DbType 设置为Object 或将CollectionType 设置为PLSQLAssociativeArray 而不设置成功。同样传递一个列表或一个对象数组而不是一个表也没有成功。

我目前不知道我还能尝试什么。

任何以高性能方式将大量实体传递给存储过程的方法都会有所帮助。我将它们与合并命令一起使用,因此我需要能够将这些参数转换为表格。

【问题讨论】:

  • SqlParameterSqlClient 库的一部分,该库专门用于 Microsoft SQL Server

标签: oracle stored-procedures .net-core odp.net ef-core-2.2


【解决方案1】:

我现在找到了一个使用临时表并将其用作输入参数的解决方案。

因为我不能传递一个完整的表格,而是一个简单对象的数组,所以我必须通过为每一列传递一个数组来填充这个表格:

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

我这样创建了临时表:

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 的工作方式与 SQL server(客户端软件)完全不同
  • 您的答案数量之多让我有点不知所措。你有什么特别的想法/你认为有比我做的更好的方法吗?
猜你喜欢
  • 1970-01-01
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
相关资源
最近更新 更多