te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c# - Composite type parameter in PostgreSql using Npgsql, - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Composite type parameter in PostgreSql using Npgsql, - Stack Overflow

programmeradmin4浏览0评论

I'm working with Npgsql and and Dapper I have some tables in PostgreSql with a composite data type:

create type datetimeoffset as
(
    "DateTimeUtc" timestamp without time zone,
    "Offset" smallint
);

To represent this data type I have the following class:

 public class PgDateTimeOffset
 {
     public PgDateTimeOffset(DateTime dateTimeUtc, short offset)
     {
         DateTimeUtc = DateTime.SpecifyKind(dateTimeUtc, DateTimeKind.Utc);
         Offset = offset;
     }

     public DateTime DateTimeUtc { get; set; }
     public short Offset { get; set; }
}

Let's say I have a table:

create table mytable
(
    Id int not null generated always as identity,
    Timestamp datetimeoffset
);

And I have code to write to this table:

public void Insert(PgDateTimeOffset timestamp)
{
    Connection.Execute("INSERT INTO mytable (Timestamp) VALUES (@timestamp)", new { timestamp });
}

I also created a type handler:

public class DapperDateTimeOffsetTypeHandler: SqlMapper.TypeHandler<DateTimeOffset>
{
    public override void SetValue(IDbDataParameter parameter, DateTimeOffset value)
    {
        // When sending data, create an instance of DateTimeOffsetPg
        parameter.Value = (PgDateTimeOffset)value;
    }

    public override DateTimeOffset Parse(object value)
    {
        // When reading data, cast the object to DateTimeOffsetPg
        if (value is PgDateTimeOffset composite)
        {
            return composite;
        }
        if(value is DateTimeOffset dto)
        {
            return dto;
        }
        if (value is DateTime dt)
        {
            return dt;
        }
        throw new DataException("Unexpected type when converting to DateTimeOffset.");
    }
}

And I am registering the type handler:

SqlMapper.AddTypeHandler(new DapperDateTimeOffsetTypeHandler());

And the composite type is registered:

var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.MapComposite<PgDateTimeOffset>("datetimeoffset");
var dataSource = dataSourceBuilder.Build();

The problem is I continue getting this exception:

System.NotSupportedException
  HResult=0x80131515
  Message=The member timestamp of type Neurologistica.Data.Repositories.PostgreSql.PgDateTimeOffset cannot be used as a parameter value
  Source=Dapper
  StackTrace:

Any idea? What am I missing?

I'm working with Npgsql and and Dapper I have some tables in PostgreSql with a composite data type:

create type datetimeoffset as
(
    "DateTimeUtc" timestamp without time zone,
    "Offset" smallint
);

To represent this data type I have the following class:

 public class PgDateTimeOffset
 {
     public PgDateTimeOffset(DateTime dateTimeUtc, short offset)
     {
         DateTimeUtc = DateTime.SpecifyKind(dateTimeUtc, DateTimeKind.Utc);
         Offset = offset;
     }

     public DateTime DateTimeUtc { get; set; }
     public short Offset { get; set; }
}

Let's say I have a table:

create table mytable
(
    Id int not null generated always as identity,
    Timestamp datetimeoffset
);

And I have code to write to this table:

public void Insert(PgDateTimeOffset timestamp)
{
    Connection.Execute("INSERT INTO mytable (Timestamp) VALUES (@timestamp)", new { timestamp });
}

I also created a type handler:

public class DapperDateTimeOffsetTypeHandler: SqlMapper.TypeHandler<DateTimeOffset>
{
    public override void SetValue(IDbDataParameter parameter, DateTimeOffset value)
    {
        // When sending data, create an instance of DateTimeOffsetPg
        parameter.Value = (PgDateTimeOffset)value;
    }

    public override DateTimeOffset Parse(object value)
    {
        // When reading data, cast the object to DateTimeOffsetPg
        if (value is PgDateTimeOffset composite)
        {
            return composite;
        }
        if(value is DateTimeOffset dto)
        {
            return dto;
        }
        if (value is DateTime dt)
        {
            return dt;
        }
        throw new DataException("Unexpected type when converting to DateTimeOffset.");
    }
}

And I am registering the type handler:

SqlMapper.AddTypeHandler(new DapperDateTimeOffsetTypeHandler());

And the composite type is registered:

var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.MapComposite<PgDateTimeOffset>("datetimeoffset");
var dataSource = dataSourceBuilder.Build();

The problem is I continue getting this exception:

System.NotSupportedException
  HResult=0x80131515
  Message=The member timestamp of type Neurologistica.Data.Repositories.PostgreSql.PgDateTimeOffset cannot be used as a parameter value
  Source=Dapper
  StackTrace:

Any idea? What am I missing?

Share Improve this question edited 2 days ago silkfire 26k16 gold badges91 silver badges114 bronze badges asked Feb 17 at 14:54 Yván EcarriYván Ecarri 1,73818 silver badges42 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

There are several things that I did to make this work.

First of all as far as I can see the parameterless ctor is required for the complex type:

public class PgDateTimeOffset
{
    public PgDateTimeOffset()
    {
        
    }

    // ...
}

Then since you don't use the standard snake-case naming for the PostgreSQL then NpgsqlNullNameTranslator usage is needed for the mapping:

dataSourceBuilder
    .MapComposite<PgDateTimeOffset>("datetimeoffset", new NpgsqlNullNameTranslator());

Then dapper mapping I used was to DbType.Object:

SqlMapper.AddTypeMap(typeof(PgDateTimeOffset), DbType.Object);

And last but not least - the I've used non-UTC time to insert:

PgDateTimeOffset timestamp = new PgDateTimeOffset
{
    DateTimeUtc = DateTime.Now,
    Offset = 3
};

The full snippet I've used for testing:

var dataSourceBuilder = new NpgsqlDataSourceBuilder(...);
dataSourceBuilder.MapComposite<PgDateTimeOffset>("datetimeoffset", new NpgsqlNullNameTranslator());
var dataSource = dataSourceBuilder.Build();
SqlMapper.AddTypeMap(typeof(PgDateTimeOffset), DbType.Object);

using var conn = dataSource.CreateConnection();
conn.Open();

PgDateTimeOffset timestamp = new PgDateTimeOffset
{
    DateTimeUtc = DateTime.Now,
    Offset = 3
};
conn.Execute("INSERT INTO mytable (Timestamp) VALUES (@timestamp)", new { timestamp });
var pgDateTimeOffsets = conn.Query<PgDateTimeOffset>("select Timestamp from mytable; ")
    .ToList();

Notes:

Since 6th version as far as I remember Npgsql maps UTC DateTime to timestamp with time zone:

The .NET and PostgreSQL types differ in the resolution and range they provide; the .NET type usually have a higher resolution but a lower range than the PostgreSQL types:

PostgreSQL type Precision/Range .NET Native Type Precision/Range
timestamp with time zone 1 microsecond, 4713BC-294276AD DateTime (UTC) 100 nanoseconds, 1AD-9999AD
timestamp without time zone 1 microsecond, 4713BC-294276AD DateTime (Unspecified) 100 nanoseconds, 1AD-9999AD
date 1 day, 4713BC-5874897AD DateOnly (6.0+), DateTime 100 nanoseconds, 1AD-9999AD
time without time zone 1 microsecond, 0-24 hours TimeOnly (6.0+), TimeSpan 100 nanoseconds, -10,675,199 - 10,675,199 days
time with time zone 1 microsecond, 0-24 hours DateTimeOffset (ignore date) 100 nanoseconds, 1AD-9999AD
interval 1 microsecond, -178000000-178000000 years TimeSpan 100 nanoseconds, -10,675,199 - 10,675,199 days

So if you want to use UTC it would be better to use timestamp with time zone for your type.

See also:

  • Need in-depth understanding of NpgSQL DateTimeOffset processing
  • How to say Datetime - timestamp without time zone in EF Core 6.0

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论