【问题标题】:Saving a DateTime to Cassandra Date column将 DateTime 保存到 Cassandra 日期列
【发布时间】:2018-10-19 23:11:59
【问题描述】:

Cassandra .NET 驱动程序文档非常糟糕,我正试图拼凑一些功能性的东西,但我浪费了很多时间试图从我找到的 Java 文档中更改代码。

我正在尝试使用 Cassandra 驱动程序将数据写入一个简单的表。该表已经存在并且里面有日期。我创建了一个映射并添加了一些列。这是一个截取的版本来演示:

For<Profile>().TableName("profiles")
    .PartitionKey(p => p.IntegerId)
    .Column(p => p.IntegerId, cm => cm.WithName("profileid"))
    .Column(p => p.BirthDate, cm => cm.WithName("dateofbirth"))

还有更多的列和表,但这是重要的部分。

然后通过一个简单的通用方法完成保存:

public async Task<bool> Add<T>(T item) where T : EntityBase, new()
{
    await _mapper.InsertIfNotExistsAsync(item);
}

这里还有更多代码,但相关部分在这里。重要的是我正在使用 InsertIfNotExists 并使用适用于基本实体的通用方法。

Cassandra 中的dateofbirth 列属于日期类型。 当我运行 Insert 方法时,我得到异常 Date 的长度应该是 4 个字节而不是 8 个(我假设我需要切断 DateTime 的时间部分)。

我尝试在映射上使用 WithType 并创建一个类似于this question 中描述的 TypeSerializer,但没有运气。 任何人都有将这种类型(可能还有其他类型)保存到 Cassandra 的工作代码?

这是从互联网改编的日期编解码器的代码,以及它是如何使用的,它可能(非常)错误:

public class DateCodec : TypeSerializer<DateTime>
{
    private static TypeSerializer<LocalDate> _innerSerializer;

    public DateCodec(TypeSerializer<LocalDate> serializer)
    {
        _innerSerializer = serializer;
        TypeInfo = new CustomColumnInfo("LocalDate");
    }

    public override IColumnInfo TypeInfo { get; }

    public override DateTime Deserialize(ushort protocolVersion, byte[] buffer, int offset, int length, IColumnInfo typeInfo)
    {
        var result = _innerSerializer.Deserialize(protocolVersion, buffer, offset, length, typeInfo);
        return new DateTime(result.Year, result.Month, result.Day);
    }

    public override ColumnTypeCode CqlType { get; }

    public override byte[] Serialize(ushort protocolVersion, DateTime value)
    {
        return _innerSerializer.Serialize(protocolVersion, new LocalDate(value.Year, value.Month, value.Day));
    }
}

用法:

TypeSerializerDefinitions definitions = new TypeSerializerDefinitions();
definitions.Define(new DateCodec(TypeSerializer.PrimitiveLocalDateSerializer));

var cluster = Cluster.Builder()
    .AddContactPoints(...)
    .WithCredentials(...)
    .WithTypeSerializers(definitions)
    .Build();

【问题讨论】:

    标签: c# cassandra


    【解决方案1】:

    C#驱动使用LocalDate类来表示来自Cassandra的date,所以要么需要修改dateofbirth的声明才能使用它,要么开发相应的编解码器。

    您可以查看有关 C# 驱动程序的日期和时间表示的文档:https://docs.datastax.com/en/developer/csharp-driver/3.5/features/datatypes/datetime/

    用代码示例更新问题后更新:

    定义表并插入示例数据:

    cqlsh> create table test.dt(id int primary key, d date);
    cqlsh> insert into test.dt(id, d) values(1, '2018-05-17');
    cqlsh> insert into test.dt(id, d) values(2, '2018-05-16');
    cqlsh> insert into test.dt(id, d) values(3, '2018-05-15');
    

    以下为我转换的作品:

    public class DateCodec : TypeSerializer<DateTime>
    {
        private static readonly TypeSerializer<LocalDate> serializer = 
             TypeSerializer.PrimitiveLocalDateSerializer;
    
        public override ColumnTypeCode CqlType
        {
            get { return ColumnTypeCode.Date; }
        }
    
        public DateCodec() { }
    
        public override DateTime Deserialize(ushort protocolVersion, byte[] buffer, 
             int offset, int length, IColumnInfo typeInfo)
        {
            var result = serializer.Deserialize(protocolVersion, buffer,
                    offset, length, typeInfo);
            return new DateTime(result.Year, result.Month, result.Day);
        }
    
        public override byte[] Serialize(ushort protocolVersion, DateTime value)
        {
            return serializer.Serialize(protocolVersion, 
                new LocalDate(value.Year, value.Month, value.Day));
        }
    }
    

    主程序:

    TypeSerializerDefinitions definitions = new TypeSerializerDefinitions();
    definitions.Define(new DateCodec());
    
    var cluster = Cluster.Builder()
             .AddContactPoints("localhost")
             .WithTypeSerializers(definitions)
             .Build();
    var session = cluster.Connect();
    var rs = session.Execute("SELECT * FROM test.dt");
    foreach (var row in rs)
    {
        var id = row.GetValue<int>("id");
        var date = row.GetValue<DateTime>("d");
        Console.WriteLine("id=" + id + ", date=" + date);
    }
    
    var pq = session.Prepare("insert into test.dt(id, d) values(?, ?);");
    var bound = pq.Bind(10, new DateTime(2018, 04, 01));
    session.Execute(bound);
    

    结果如下:

    id=1, date=5/17/18 12:00:00 AM
    id=2, date=5/16/18 12:00:00 AM
    id=3, date=5/15/18 12:00:00 AM
    

    并从cqlsh检查:

    cqlsh> SELECT * from test.dt ;
    
     id | d
    ----+------------
     10 | 2018-04-01
      1 | 2018-05-17
      2 | 2018-05-16
      3 | 2018-05-15
    

    【讨论】:

    • 感谢您的回答。正如我在原始消息中所说,我确实尝试创建一个编解码器来转换日期,但它不起作用。我将更新帖子以添加编解码器调用及其调用方式。
    • 它怎么不工作 - 抛出错误,根本不转换等?
    • 几乎没有它的行为就像没有它一样,但代码确实执行(序列化方法在保存时在调试器中调用),但我仍然收到异常说 Date 应该是 4 个字节长。
    • 我已经得到了它的小修改 - 请参阅更新的代码...关于示例 - 通常我会为它们查看测试套件:对于 C#,这是 github.com/datastax/csharp-driver/tree/master/src/…github.com/datastax/csharp-driver/tree/master/src/…
    • 是的,确实有效。似乎我唯一缺少的就是为 CqlType 属性设置 getter。还要感谢您提供检查测试套件的提示。
    猜你喜欢
    • 2016-08-05
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 2016-01-30
    相关资源
    最近更新 更多