如何使用 Dapper 将 JSON 作为原始 PostgreSQL 类型传递给函数?

dev*_*309 4 c# postgresql dapper

我有一个 PostgreSQL 函数,它接受一个类型为 的参数json。使用 Dapper,我如何执行将对象传递给 PostgreSQL 函数的调用,以便 PostgreSQL 将类型识别为 asjson而不是 as text

接受 json 类型的 PostgreSQL 函数示例

CREATE OR REPLACE FUNCTION testfuncthattakesinjson(heroes json)
    RETURNS SETOF characters 
    LANGUAGE 'sql'

    STABLE
    ROWS 100
AS $BODY$

    SELECT  c.*
    FROM    characters c
    JOIN    json_array_elements(heroes) j
    ON      c.first_name = j->>'first_name'
    AND     c.last_name = j->>'last_name';

$BODY$;
Run Code Online (Sandbox Code Playgroud)

破碎的示例 C# 集成测试

[Test]
public void Query_CallFunctionThatTakesInJsonParameter_FunctionUsesJsonType()
{
    using (var conn = new NpgsqlConnection(Db.GetConnectionStringToDatabase()))
    {
        var funcName = "testfuncthattakesinjson";
        var expect = CharacterTestData.Where(character => character.id <= 3);
        var jsonObj = JArray.FromObject(CharacterTestData
            .Where(character => character.id <= 3)
            .Select(character => new Hero(character))
            .ToList());

        SqlMapper.AddTypeHandler(new JArrayTypeHandler());

        // Act 
        var results = conn.Query<Character>(funcName, new
        {
            heroes = jsonObj
        }, commandType: CommandType.StoredProcedure);

        // Assert
        CollectionAssert.AreEquivalent(expect, results);
    }
}
Run Code Online (Sandbox Code Playgroud)

支持 JArrayTypeHandler

internal class JArrayTypeHandler : SqlMapper.TypeHandler<JArray>
{
    public override JArray Parse(object value)
    {
        throw new NotImplementedException();
    }

    public override void SetValue(IDbDataParameter parameter, JArray value)
    {
        parameter.Value = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

在当前的迭代中,我添加了一个SqlMapper.TypeHandler. (目前,我只关心将 传递JArray PostgreSQL,因此是NotImplmentedExceptionfor Parse。)

在这个例子中,我得到以下异常:

System.NotSupportedException : 'Npgsql 或您的 PostgreSQL 不支持 CLR 数组类型 Newtonsoft.Json.Linq.JArray。如果您希望将其映射到 PostgreSQL 复合类型数组,您需要在使用前注册它,请参阅文档。

在过去的迭代中,我还尝试过使用List<Hero>类型处理程序并让该类型处理程序处理 Json 转换等方法。

我还尝试Npgsql.Json.NET为 Npgsql添加Nuget 包扩展并调用conn.TypeMapper.UseJsonNet()我的测试方法,但这似乎没有任何效果。

如果我执行任何操作将对象序列化为 JSON 字符串,那么我会收到一个不同的错误(如下),这是有道理的。

Npgsql.PostgresException : '42883: 函数 testfuncthattakesinjson(heroes => text) 不存在'

那么,是否可以使用 Dapper 将 JSON 对象作为 PostgreSQL 原语传递给函数?

小智 5

您可以使用 Dapper 的 ICustomQueryParameter 接口。

public class JsonParameter : ICustomQueryParameter
{
    private readonly string _value;

    public JsonParameter(string value)
    {
        _value = value;
    }

    public void AddParameter(IDbCommand command, string name)
    {
        var parameter = new NpgsqlParameter(name, NpgsqlDbType.Json);
        parameter.Value = _value;

        command.Parameters.Add(parameter);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的 Dapper 电话变成:

var results = conn.Query<Character>(funcName, new
    {
        heroes = new JsonParameter(jsonObj.ToString())
    }, commandType: CommandType.StoredProcedure);
Run Code Online (Sandbox Code Playgroud)