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
    );