【DM版本】: DM Database Server 64 V8
【操作系统】:麒麟 V10
【CPU】: Kunpeng-920
【驱动】: NuGet Gallery | DM.DmProvider 8.3.1.37876
【问题描述】*:如何通过 ado.net 向存储过程传自定义类型参数?
我已经看到社区内的相关问答 存储过程中传参为自定义类型,如何解决? | 达梦技术社区,但是并没有获得有用信息。
public class GeneralType {
public int Id { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Value4 { get; set; }
public string Value5 { get; set; }
}
-- 定义复合类型 - row
CREATE OR REPLACE TYPE GeneralTableRow AS OBJECT (
"Id" INT,
"Value1" VARCHAR(32767), -- VARCHAR(32767) 是C# string类型自动映射到达梦数据库的默认类型
"Value2" VARCHAR(32767),
"Value3" VARCHAR(32767),
"Value4" VARCHAR(32767),
"Value5" VARCHAR(32767)
);
-- 定义复合类型 - table
CREATE OR REPLACE TYPE GeneralTableType AS TABLE OF GeneralTableRow;
CREATE TABLE "TestEntities"(
"Id" INT IDENTITY(1, 1),
"Code" VARCHAR2(100),
"Name" VARCHAR2(300),
"DateTimeValue" TIMESTAMP(6) NOT NULL,
"DecimalValue" DECIMAL(12,2) NOT NULL,
"BooleanValue" BIT DEFAULT 0 NOT NULL,
CONSTRAINT "PK_TestEntities" NOT CLUSTER PRIMARY KEY("Id")
) STORAGE(ON "MAIN", CLUSTERBTR);
CREATE OR REPLACE PROCEDURE UpdateTestEntities(
data IN GeneralTableType -- 批量更新数据(自定义表类型)
)
AS
BEGIN
-- 遍历批量数据,逐行更新(FORALL批量更新,效率更高,且为事务操作)
FORALL i IN 1..data.COUNT
UPDATE "TestEntities" SET
"Code" = data(i).Value1,
"Name" = data(i).Value2,
"DateTimeValue" = TO_TIMESTAMP(data(i).Value3, 'YYYY-MM-DD HH24:MI:SS.FF6'), -- 转换为TIMESTAMP
"DecimalValue" = TO_NUMBER(data(i).Value4, '9999999999.99'), -- 转换为DECIMAL
"BooleanValue" = CAST(data(i).Value5 AS BIT)
WHERE "Id" = data(i).Id;
END;
✅ 代码能正常执行,且表中数据被修改,符合预期。
DECLARE
-- data_list 是我们本地的变量名
data_list GENERALTABLETYPE;
-- 声明一个变量来存储受影响的行数(可选,存储过程本身不返回,但 FORALL 会影响 SQL%ROWCOUNT)
v_rows_affected INT;
BEGIN
-- 初始化集合变量
data_list := GENERALTABLETYPE();
-- 向集合中添加数据行
-- 确保 Id 存在于您的 TestEntities 表中
data_list.EXTEND(2); -- 扩展集合以容纳 2 行数据
-- 插入第一行数据
data_list(1) := GENERALTABLEROW(
4, -- Id
'CODE_A_DB', -- Value1 (Code)
'Name A Updated DB Call', -- Value2 (Name)
'2025-12-01 10:00:00.000', -- Value3 (DateTimeValue - 字符串格式)
'150.75', -- Value4 (DecimalValue - 字符串格式)
'1' -- Value5 (BooleanValue - 字符串 '1' 或 '0')
);
-- 插入第二行数据
data_list(2) := GENERALTABLEROW(
5, -- Id
'CODE_B_DB', -- Value1
'Name B Updated DB Call', -- Value2
'2025-12-01 11:00:00.000', -- Value3
'200.00', -- Value4
'0' -- Value5
);
-- 调用存储过程,并将准备好的集合作为参数传入
UpdateTestEntities(data_list);
-- 可选:打印或查看受影响的总行数
-- 在达梦 PL/SQL 中,FORALL 后的 SQL%ROWCOUNT 表示总数
v_rows_affected := SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('Total affected rows: ' || v_rows_affected);
-- 提交事务
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
ROLLBACK;
END;
// [TestMethod]
public async Task StoredProcedureWithComplexTypeParameterTest()
{
var data = new List<GeneralType>
{
new GeneralType
{
Id = 4,
Value1 = "CODE004",
Value2 = "Test Entity updated by ado.net",
Value3 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff"),
Value4 = "100.00",
Value5 = true.ToString()
},
new GeneralType
{
Id = 5,
Value1 = "CODE005",
Value2 = "Test Entity updated by ado.net",
Value3 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff"),
Value4 = "200.00",
Value5 = false.ToString()
}
};
var conn = new DmConnection(Configuration.GetConnectionString("Dm"));
var command = new DmCommand("UpdateTestEntities", conn)
{
CommandType = System.Data.CommandType.StoredProcedure,
};
var parameter = new DmParameter()
{
ParameterName = "data",
DbType = System.Data.DbType.Object,
DmSqlTypeName = "GENERALTABLETYPE",
Value = data
};
command.Parameters.Add(parameter);
await command.ExecuteNonQueryAsync();
}
❌ List<GeneralType>: parameter.Value = data;
消息:
Test method DmAdoTests.StoredProcedureTests.StoredProcedureWithComplexTypeParameterTest threw exception:
System.SystemException: Value is of unknown data type
堆栈跟踪:
DmCommand.BindParameters(Int32& rowCount, DmDataReader rd, CommandBehavior behavior)
DmCommand.do_ExecuteNonQuery()
DmCommand.ExecuteNonQuery()
DbCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)
❌ Array<GeneralType>: parameter.Value = data.ToArray();
消息: Test method DmAdoTests.StoredProcedureTests.StoredProcedureWithComplexTypeParameterTest threw exception: System.Exception: 非Array类型入参无法使用Array进行绑定 堆栈跟踪: DmCommand.BindParameters(Int32& rowCount, DmDataReader rd, CommandBehavior behavior) DmCommand.do_ExecuteNonQuery() DmCommand.ExecuteNonQuery() DbCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)
❌ json-string: parameter.Value = System.Text.Json.JsonSerializer.Serialize(data);
消息: Test method DmAdoTests.StoredProcedureTests.StoredProcedureWithComplexTypeParameterTest threw exception: System.InvalidCastException: Specified cast is not valid. 堆栈跟踪: DmCommand.BindParameters(Int32& rowCount, DmDataReader rd, CommandBehavior behavior) DmCommand.do_ExecuteNonQuery() DmCommand.ExecuteNonQuery() DbCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)
❌ 转换为DataTable: parameter.Value = MetadataHelper.ToDataTable(data);
消息:
Test method DmAdoTests.StoredProcedureTests.StoredProcedureWithComplexTypeParameterTest threw exception:
System.SystemException: Value is of unknown data type
堆栈跟踪:
DmCommand.BindParameters(Int32& rowCount, DmDataReader rd, CommandBehavior behavior)
DmCommand.do_ExecuteNonQuery()
DmCommand.ExecuteNonQuery()
DbCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)
如果您有解决方案或者任何看法,请不吝赐教,期待在评论区看到您的留言 🌹🌹🌹

使用这个代码,应该就没有问题,本地已测试过了。