C# 呃TypeHandler要工作,我们需要以下格式的结果: |-----------------| | heroes | |-----------------| | (Peter, Parker) | |-----------------| |------|-----------------| | year | hero | |------|-----------------| | 1962 | (Peter, Parker) | |------|-----------------|
这是因为在像C# 呃TypeHandler要工作,我们需要以下格式的结果: |-----------------| | heroes | |-----------------| | (Peter, Parker) | |-----------------| |------|-----------------| | year | hero | |------|-----------------| | 1962 | (Peter, Parker) | |------|-----------------|,c#,postgresql,dapper,C#,Postgresql,Dapper,这是因为在像conn.Query(…)这样的调用之后,Dapper会将第一列(并且只有第一列)传递给TypeHandler,希望它进行必要的转换 但是,上述函数的输出,example\u function,实际上将以以下扩展格式返回结果: |------------|-----------| | first_name | last_name | |------------|-----------| | Peter | Parker | |------------|--------
conn.Query(…)
这样的调用之后,Dapper会将第一列(并且只有第一列)传递给TypeHandler
,希望它进行必要的转换
但是,上述函数的输出,example\u function
,实际上将以以下扩展格式返回结果:
|------------|-----------|
| first_name | last_name |
|------------|-----------|
| Peter | Parker |
|------------|-----------|
这意味着传递给TypeHandler.Parse()
方法的value
的Type
在本例中是string
但是,对于在返回两列或多列时将UDT作为其中一列返回的函数,TypeHandler
会按预期工作,因为单个列的值会传递给Parse
方法
考虑此更新的功能:
创建或替换函数示例\u函数()
返回表(年份整数,英雄)
语言SQL
作为
$$
选择1962年作为年份(‘彼得’、‘帕克’)::英雄作为英雄
$$
以以下格式返回输出:
|-----------------|
| heroes |
|-----------------|
| (Peter, Parker) |
|-----------------|
|------|-----------------|
| year | hero |
|------|-----------------|
| 1962 | (Peter, Parker) |
|------|-----------------|
这就是以下原始解决方案的不足之处。在那个例子中,我(还)没有使用Parse
方法。但是,一旦我需要实现该函数以支持返回的UDT,TypeHandler
将无法按照PostgreSQL返回UDT的方式工作,如上所示
原始答案 对于其他可能偶然发现这个问题的人来说,这对我来说很有效,尽管我对这个问题不是很满意,所以我愿意找到更好的解决方案 工作集成测试
[测试]
public void Query\u callfunctionthatakesinserdefinedtype\u functionusesusserdefinedtype()
{
//安排
使用(var conn=new NpgsqlConnection(Db.GetConnectionStringToDatabase())
{
var funcName=“testfunchattakesinudt”;
var expect=CharacterTestData.First();
AddTypeHandler(新的HeroTypeHandler());
conn.Open();
连接重新加载类型();
conn.TypeMapper.MapComposite(“英雄”);
//表演
var result=conn.Query(funcName,
新的
{
我们的英雄=新英雄
{
first\u name=CharacterTestData.first()。first\u name,
last\u name=CharacterTestData.First()。last\u name
}
},
commandType:commandType.StoredProcess
).FirstOrDefault();
//断言
断言.AreEqual(期望、结果);
}
}
支持HeroTypeHandler
内部类HeroTypeHandler:SqlMapper.TypeHandler
{
公共覆盖英雄解析(对象值)
{
抛出新的NotImplementedException();
}
公共覆盖无效设置值(IDbDataParameter参数,Hero值)
{
参数值=值;
}
}
解决方案似乎分为两部分:
HeroTypeHandler
,并通过SqlMapper.AddTypeHandler
对其进行映射conn.TypeMapper.MapComposite
将我的类型Hero
映射到我的PostgreSQL复合类型Hero
。然而,我的直觉(到目前为止)是,这只是我在进行自己的集成测试,因为在实际的应用程序中,在应用程序开始时全局注册所有复合类型可能是理想的。(如果有很多,可能不是因为性能原因?)HeroTypeHandler
实际上没有提供任何真正的价值(没有双关语)。只需将value
赋值给parameter.value
就可以了,我猜这是Dapper为调用所做的事情,但显然不是。(?)
请注意,由于我只关心将此类型发送到PostgreSQL函数,因此我认为没有必要实现Parse
方法,因此使用NotImplementedException
。YMMV
另外,由于在我发布原始问题后进行了一些重构,因此还有一些其他的小差异。但是,它们与上面详述的整体修复程序无关。而不是
SqlMapper.TypeHandler
或ICustomQueryParameter
你可以用
Dapper.SqlMapper.AddTypeMap(typeof(Hero), DbType.Object);
我在应用程序中使用了更简单的解决方案: 在DbContext或应用程序的开头设置UDT映射
NpgsqlConnection.GlobalTypeMapper.MapComposite<MyContactParameter>("udt_my_contact");
使用Dapper调用DB过程并传递该UDT类型的数组
await dapperConnection.ExecuteAsync(procedureName, new
{
param_first = "string anything",
param_udt_list = new List<MyContactParameter>{
new MyContactParameter {
id = Guid.NewId(),
name = "any name",
company_name = "any company name",
is_something = true
},
new MyContactParameter {
id = Guid.NewId(),
name = "any name 2",
company_name = "any company name 2",
is_something = false
}
}
},
commandType: CommandType.StoredProcedure
);
等待DappeConnection.ExecuteAsync(过程重命名,新建
{
param_first=“字符串任何内容”,
param_udt_list=新列表{
新的MyContactParameter{
id=Guid.NewId(),
name=“任意名称”,
公司名称=“任何公司名称”,
这是真的吗
},
新的MyContactParameter{
id=Guid.NewId(),
name=“任意名称2”,
公司名称=“任何公司名称2”,
什么东西是假的吗
}
}
},
commandType:commandType.StoredProcess
);