Ich habe eine PostgreSQL-Funktion, die einen Parameter vom Typ json
aufnimmt. Wie führe ich mit Dapper einen Aufruf aus, der das Objekt an die PostgreSQL-Funktion weiterleitet, sodass PostgreSQL den Typ als json
statt als text
erkennt?
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$;
[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);
}
}
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;
}
}
In dieser aktuellen Iteration habe ich einen SqlMapper.TypeHandler
hinzugefügt. (Im Moment JArray
ich mich nur mit der Übergabe des JArray
an PostgreSQL, daher die NotImplmentedException
für Parse
.)
Mit diesem Beispiel erhalte ich die folgende Ausnahme:
System.NotSupportedException : 'Der CLR-Array-Typ Newtonsoft.Json.Linq.JArray wird von Npgsql oder PostgreSQL nicht unterstützt. Wenn Sie es einem zusammengesetzten PostgreSQL-Array zuordnen möchten, müssen Sie es vor der Verwendung registrieren. Weitere Informationen finden Sie in der Dokumentation. '
In früheren Iterationen habe ich auch versucht, einen List<Hero>
-Typ-Handler zu verwenden und diesen Typ-Handler mit der Json-Konvertierung umgehen zu lassen.
Ich habe auch versucht, die Npgsql.Json.NET
Paketerweiterung für Npgsql hinzuzufügen und conn.TypeMapper.UseJsonNet()
in meiner Testmethode conn.TypeMapper.UseJsonNet()
, aber das schien keine Auswirkungen zu haben.
Wenn ich etwas unternehme, um das Objekt in einen JSON-String zu serialisieren, wird ein anderer Fehler angezeigt (siehe unten). Dies ist sinnvoll.
Npgsql.PostgresException : '42883: Funktion testfuncthattakesinjson (heroes => text) existiert nicht'
Ist es also möglich, mit Dapper ein JSON-Objekt als PostgreSQL-Grundelement an eine Funktion zu übergeben?
Sie können die ICustomQueryParameter-Schnittstelle von Dapper verwenden.
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);
}
}
Dann wird Ihr Dapper-Anruf zu:
var results = conn.Query<Character>(funcName, new
{
heroes = new JsonParameter(jsonObj.ToString())
}, commandType: CommandType.StoredProcedure);