You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过C#的Dapper向PL/SQL的RECORD类型变量传递数据?

我之前也踩过Dapper和PL/SQL RECORD类型交互的坑,确实挺头疼的——Dapper对Oracle的RECORD类型支持有限,不过有几个实用的解决方案,根据你的情况选就行:

方案1:用自定义OBJECT类型替代RECORD(推荐)

PL/SQL的RECORD是本地类型,没法直接被外部程序(比如C#)识别,但自定义的OBJECT类型是数据库级别的,Dapper可以很好地支持它,还能和C#类直接映射。

步骤1:在Oracle中创建对应OBJECT类型

假设你的原RECORD结构是这样的:

-- 原RECORD类型
TYPE EMP_REC_TYPE IS RECORD (
    EMP_ID NUMBER,
    EMP_NAME VARCHAR2(100),
    DEPT_ID NUMBER
);

改成数据库级的OBJECT:

CREATE OR REPLACE TYPE EMP_REC AS OBJECT (
    EMP_ID NUMBER,
    EMP_NAME VARCHAR2(100),
    DEPT_ID NUMBER
);
/
-- 另外两个RECORD同理创建对应的OBJECT,比如DEPT_REC、SAL_REC

步骤2:修改存储过程参数为OBJECT类型

CREATE OR REPLACE PROCEDURE PROC_EMP_OPERATE (
    P_EMP EMP_REC,
    P_DEPT DEPT_REC,
    P_SAL SAL_REC
) AS
BEGIN
    -- 你的业务逻辑,比如插入数据
    INSERT INTO EMPLOYEES VALUES (P_EMP.EMP_ID, P_EMP.EMP_NAME, P_EMP.DEPT_ID);
    INSERT INTO DEPARTMENTS VALUES (P_DEPT.DEPT_ID, P_DEPT.DEPT_NAME);
    INSERT INTO SALARIES VALUES (P_SAL.EMP_ID, P_SAL.SAL_AMOUNT);
END;
/

步骤3:C#端创建对应类并传参

先定义和OBJECT字段匹配的C#类:

public class EmpRec
{
    public int EmpId { get; set; }
    public string EmpName { get; set; }
    public int DeptId { get; set; }
}

public class DeptRec
{
    public int DeptId { get; set; }
    public string DeptName { get; set; }
}

public class SalRec
{
    public int EmpId { get; set; }
    public decimal SalAmount { get; set; }
}

然后用Dapper的DynamicParameters指定Oracle类型和OBJECT名称:

var empData = new EmpRec { EmpId = 101, EmpName = "张三", DeptId = 5 };
var deptData = new DeptRec { DeptId = 5, DeptName = "技术部" };
var salData = new SalRec { EmpId = 101, SalAmount = 8000 };

using var conn = new OracleConnection("你的连接字符串");
var parameters = new DynamicParameters();

// 关键:指定typeName为Oracle中创建的OBJECT名称
parameters.Add("P_EMP", empData, OracleDbType.Object, ParameterDirection.Input, typeName: "EMP_REC");
parameters.Add("P_DEPT", deptData, OracleDbType.Object, ParameterDirection.Input, typeName: "DEPT_REC");
parameters.Add("P_SAL", salData, OracleDbType.Object, ParameterDirection.Input, typeName: "SAL_REC");

conn.Execute("PROC_EMP_OPERATE", parameters, commandType: CommandType.StoredProcedure);
方案2:直接构造PL/SQL块调用(无需修改存储过程)

如果不能修改现有存储过程的RECORD参数,那可以直接在C#里构造PL/SQL块,手动把C#数据赋值给RECORD变量,再调用存储过程。

比如你的原存储过程是:

CREATE OR REPLACE PROCEDURE PROC_EMP_OPERATE (
    P_EMP EMP_REC_TYPE,
    P_DEPT DEPT_REC_TYPE,
    P_SAL SAL_REC_TYPE
) AS
BEGIN
    -- 业务逻辑
END;
/

C#端这样写:

var empId = 101;
var empName = "张三";
var deptId = 5;
var deptName = "技术部";
var salAmount = 8000;

using var conn = new OracleConnection("你的连接字符串");
var plsqlBlock = @"
DECLARE
    V_EMP EMP_REC_TYPE;
    V_DEPT DEPT_REC_TYPE;
    V_SAL SAL_REC_TYPE;
BEGIN
    -- 给RECORD变量赋值
    V_EMP.EMP_ID := :EmpId;
    V_EMP.EMP_NAME := :EmpName;
    V_EMP.DEPT_ID := :DeptId;
    
    V_DEPT.DEPT_ID := :DeptId;
    V_DEPT.DEPT_NAME := :DeptName;
    
    V_SAL.EMP_ID := :EmpId;
    V_SAL.SAL_AMOUNT := :SalAmount;
    
    -- 调用存储过程
    PROC_EMP_OPERATE(V_EMP, V_DEPT, V_SAL);
END;";

var parameters = new DynamicParameters();
parameters.Add("EmpId", empId);
parameters.Add("EmpName", empName);
parameters.Add("DeptId", deptId);
parameters.Add("DeptName", deptName);
parameters.Add("SalAmount", salAmount);

conn.Execute(plsqlBlock, parameters);

这个方法的好处是不用改存储过程,缺点是如果RECORD字段多的话,赋值代码会比较繁琐。

方案3:数组传参+存储过程内循环映射(批量场景)

如果是要批量传递多个RECORD数据,你之前尝试的数组传参思路是对的,只是需要在存储过程里循环把数组元素映射到RECORD变量。

步骤1:修改存储过程为数组参数

CREATE OR REPLACE PROCEDURE PROC_BATCH_EMP_OPERATE (
    P_EMP_IDS SYS.ODCINUMBERLIST,
    P_EMP_NAMES SYS.ODCIVARCHAR2LIST,
    P_DEPT_IDS SYS.ODCINUMBERLIST,
    P_SAL_AMOUNTS SYS.ODCINUMBERLIST
) AS
    TYPE EMP_REC_TYPE IS RECORD (
        EMP_ID NUMBER,
        EMP_NAME VARCHAR2(100),
        DEPT_ID NUMBER,
        SAL_AMOUNT NUMBER
    );
    V_EMP EMP_REC_TYPE;
BEGIN
    -- 循环处理每一组数据
    FOR I IN 1..P_EMP_IDS.COUNT LOOP
        V_EMP.EMP_ID := P_EMP_IDS(I);
        V_EMP.EMP_NAME := P_EMP_NAMES(I);
        V_EMP.DEPT_ID := P_DEPT_IDS(I);
        V_EMP.SAL_AMOUNT := P_SAL_AMOUNTS(I);
        
        -- 执行业务逻辑,比如插入
        INSERT INTO EMPLOYEES VALUES (V_EMP.EMP_ID, V_EMP.EMP_NAME, V_EMP.DEPT_ID);
        INSERT INTO SALARIES VALUES (V_EMP.EMP_ID, V_EMP.SAL_AMOUNT);
    END LOOP;
END;
/

步骤2:C#端传递数组参数

var empIds = new int[] {101, 102, 103};
var empNames = new string[] {"张三", "李四", "王五"};
var deptIds = new int[] {5, 5, 6};
var salAmounts = new decimal[] {8000, 7500, 9000};

using var conn = new OracleConnection("你的连接字符串");
var parameters = new DynamicParameters();

parameters.Add("P_EMP_IDS", empIds, OracleDbType.Int32, ParameterDirection.Input);
parameters.Add("P_EMP_NAMES", empNames, OracleDbType.Varchar2, ParameterDirection.Input);
parameters.Add("P_DEPT_IDS", deptIds, OracleDbType.Int32, ParameterDirection.Input);
parameters.Add("P_SAL_AMOUNTS", salAmounts, OracleDbType.Decimal, ParameterDirection.Input);

conn.Execute("PROC_BATCH_EMP_OPERATE", parameters, commandType: CommandType.StoredProcedure);

注意要保证所有数组的长度一致,否则循环会出问题。


内容的提问来源于stack exchange,提问作者CrazyWu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:32:15