如何通过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
相关产品推荐
相关产品推荐

