Npgsql C#-将参数作为复合类型的数组传递到存储过程

时间:2020-08-23 11:53:52

标签: c# postgresql npgsql asp.net-core-3.1 postgresql-12

此主题可能与此Array of composite type as stored procedure input passed by C# Npgsql重复。但这是2017年的旧版本,某些API和属性已弃用。

当前,我正在尝试将一组复合类型传递给存储过程。我确实映射了一个全局复合类型。但是发生了一个异常无法使用处理程序类型MappedCompositeHandler`1编写CLR类型Web.API.Models.UdtSpParameter []

我尝试用google搜索,似乎找不到任何解决方法。下面是我创建,映射,调用命令存储过程的内容。

PostgreSQL

/* Create composite type */
CREATE TYPE udt_sp_parameter AS (
    field VARCHAR(50),
    value VARCHAR
);


/* Create stored procedure */
CREATE OR REPLACE PROCEDURE stored_example(
    parameters udt_sp_parameter[])
LANGUAGE 'plpgsql'

AS $BODY$
DECLARE 
    _refCursor CURSOR FOR SELECT field, value FROM UNNEST(parameters::udt_sp_parameter[]);

    _record udt_sp_parameter;
BEGIN 
    OPEN _refCursor;

    LOOP
        FETCH _refCursor INTO _record;
        IF NOT FOUND THEN 
            EXIT;
        END IF;
  
        RAISE NOTICE 'Field: %', _record.field;
        RAISE NOTICE 'Value: %', _record.value IS NULL;
    END LOOP;

    CLOSE _refCursor;
END;
$BODY$;

然后我尝试调用plpgsql语言存储的并且运行良好。

DO
$$
DECLARE
parameters udtt_input_param[] := ARRAY[
     ROW('YED','Yeti')
    ,ROW('INTELLIGENT','NOOB')
    ,ROW('ZXC',NULL)
    ,ROW('CXX','1')];

BEGIN

CALL stored_example(parameters);
END
$$
LANGUAGE plpgsql

C#Npgsql (nuget Npgsql 4.1.4)

// mapping global type on Startup.cs
NpgsqlConnection.GlobalTypeMapper.MapComposite<UdtSpParameter>("udt_sp_parameter");

// class model UdtSpParameter
public class UdtSpParameter
{
    [PgName("field")]
    public string Field { get; set; }
    [PgName("value")]
    public string Value { get; set; }

    public UdtSpParameter() { }
}

// call stored procedure at data access layer for example StudentDAL.cs
public IEnumerable<T> CallStoredResultSet<T>(UdtSpParameter[] inputParameters ) where T : class
{
    var conn = _GetOpenConnection();
    var tran = _BeginTransaction(conn);

    NpgsqlCommand command = new NpgsqlCommand("stored_example", conn);
    command.CommandType = CommandType.StoredProcedure;
    var cmdParam = command.CreateParameter();
    cmdParam.ParameterName = "parameters";
    cmdParam.DbType = DbType.Object;  
    cmdParam.Value = inputParameters;
    cmdParam.DataTypeName = "udt_sp_parameter";

    command.Parameters.Add(cmdParam);

    // throw exception here
    // Can't write CLR type Web.API.Models.UdtSpParameter[] with handler type MappedCompositeHandler`1
    NpgsqlDataReader dr = command.ExecuteReader(); 


    var result = new List<T>();

    while (dr.Read())
    {
        Console.WriteLine(dr[0].ToString(), dr[1].ToString());
    }


    _CommitTransaction(tran);
    _CloseConnection(conn);

    return result;
}

如果我做错了任何事情,请找点东西,并指出要解决的地方。预先感谢。

2 个答案:

答案 0 :(得分:1)

Npgsql 的官方文档说:

<块引用>

调用存储过程的唯一方法是编写自己的CALL my_proc(...) 命令,不设置 CommandBehavior.StoredProcedure。

在您的特定情况下,您应该像这样修改代码:

NpgsqlCommand command = new NpgsqlCommand("call stored_example(:parameters)", conn);
// comment this line command.CommandType = CommandType.StoredProcedure;

希望对兄弟有帮助。

答案 1 :(得分:0)

出现此错误是因为您为参数指定了类型名称,该名称是复合类型而不是复合数组。因此,您应该将udt_sp_parameter[]的值指定为DataTypeName而不是udt_sp_parameter

var cmdParam = command.CreateParameter();
cmdParam.ParameterName = "parameters";
cmdParam.Value = inputParameters;
cmdParam.DataTypeName = "udt_sp_parameter[]";

由于您的类型已注册,并且驱动程序已经知道其PostgreSQL名称,因此无需在参数设置过程中指定它。随意从上面的代码中删除最后一行:

var cmdParam = command.CreateParameter();
cmdParam.ParameterName = "parameters";
cmdParam.Value = inputParameters;

在大多数情况下,驱动程序足够聪明以自动检测类型,但是如果存在歧义,则应指定该类型。例如,您有一个字符串应作为JSON传递,或将CLR类型序列化为JSON。在其他情况下,只需依靠驾驶员内部,然后允许他完成工作即可。

相关问题