如何在聚合过程中反序列化DateTimeOffset



我有以下代码:

class TheThing
{
public int Number { get; set; }
public DateTimeOffset Date { get; set; }
}
static void Main(string[] args)
{
var client = new MongoClient();
var database = client.GetDatabase("test");
var collection = database.GetCollection<TheThing>("theThings");
var theThing = new TheThing()
{
Number = 1,
Date = DateTimeOffset.UtcNow
};
collection.InsertOne(theThing);
var theFirstItem = collection.Aggregate()
.Group(new BsonDocument { { "_id", "Number" }, { "firstDate", new BsonDocument { { "$first", "$Date" } } } })
.First();
var firstDate = theFirstItem["firstDate"].ToUniversalTime();
}

数据库中保存的文档如下。

{
"_id" : ObjectId("62ba1b93fd0318e0c3db5935"),
"Number" : 1,
"Date" : [ 
NumberLong(637919607231619015), 
0
]
}

聚合管道生成的文档类似于:

{
"_id" : 1,
"firstDate" : [ 
NumberLong(637919607231619015), 
0
]
}
不管怎样,最后一行抛出了一个异常:

System.NotSupportedException: 'BsonArray does not support ToUniversalTime.'

如何在聚合后获得DateTimeOffset ?

主要问题是MongoDB将DateTimeOffset序列化为tick(长/Int64)和offset(以分钟为单位)的BsonArray。如果你不想改变它,你可以像这样反序列化它:

var array = theFirstItem["firstDate"].AsBsonArray;
var timestamp = array[0].AsInt64;
var offset = TimeSpan.FromMinutes(array[1].AsInt32);
var dto = new DateTimeOffset(timestamp, offset);

在MQL聚合中,您可以使用类似于以下代码来计算UTC时间(不幸的是,.NET使用tick since 0001-01-01 00:00而MongoDB使用Unix Epoch als参考点):

[{$set: {
dt: {
$toDate: {
$subtract: [
{ $subtract: [
{ $divide: [ { $first: '$Date' }, 10000 ] }, // Convert ticks to ms
62135596800000 ] // Convert to Unix epoch
},
{
$multiply: [
{ $last: '$Date' }, 60000 ] // respect offset
}]
}
}
}}]

如果你可以改变数据的存储方式,并且你不需要偏移量部分,你可以像这样调整值的序列化:

[BsonRepresentation(BsonType.DateTime)]
public DateTimeOffset Date { get; set; }

由于使用BsonRepresentation属性,MongoDB将该值存储为UTC日期时间,而不带偏移量。这样做的好处是,数据库中的ISODate可以很容易地用于MongoDB查询。因为所有的日期值都是用UTC存储的,所以它们很容易比较。

然而,如果你不能没有偏移部分,另一个选择是自定义BsonArray-DateTimeOffset的反序列化,以便在反序列化期间完成到DateTimeOffset的转换。详情请参阅此链接

最新更新