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

时间:2019-05-23 22:25:09

标签: c# postgresql dapper

我有一个PostgreSQL函数,带有一个json类型的参数。使用Dapper,如何执行将对象传递给PostgreSQL函数的调用,以使PostgreSQL将类型识别为json而不是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$;

残破的示例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);
    }
}

支持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;
    }
}

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

在此示例中,我得到以下异常:

  

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

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

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

如果我做任何将对象序列化为JSON字符串的操作,那么我将得到另一个错误(如下所示),这很有意义。

  

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

因此,可以使用Dapper将JSON对象作为PostgreSQL原语传递给函数吗?

1 个答案:

答案 0 :(得分:3)

您可以使用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);
    }
}

然后,您的Dapper呼叫将变为:

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