Cosmos DB DocumentClient 的 DateTime 处理中的错误
Bug in DateTime handling of Cosmos DB DocumentClient
此问题与 Microsoft.Azure.DocumentDB.Core v2.11.2
中的 DocumentClient
有关。 (更新:该错误也存在于 Microsoft.Azure.Cosmos
。)
当查询包含带尾随零的 DateTime
值时,Cosmos DB 的 LINQ 提供程序中似乎存在错误。考虑以下代码:
string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234560Z"; // trailing zero will be truncated by LINQ provider :-(
DateTime datetime = DateTime.Parse(dateTimeWithTrailingZero, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
IQueryable<Dictionary<string, object>> query =
client.CreateDocumentQuery<Dictionary<string, object>>(collectionUri)
.Where(x => (DateTime) x["datetime"] <= datetime);
query
的结果包括 个文档,其中 属性 datetime
例如"2000-01-01T00:00:00.1234567Z"
(尽管 不应该 )。
query
的结果 不包括 文档,其中 datetime
是 "2000-01-01T00:00:00.1234560Z"
(即使它 应该).
有什么方法可以使用 DocumentClient
和 LINQ 正确查询 DateTime
属性吗? (我知道使用原始 SQL 是有效的——出于各种原因,我 必须 使用 LINQ/IQueryable
。)
LINQ 是 removing/truncating 'Z' 之前的最后一个“0”,因此最终查询的工作方式如下:
query":"SELECT VALUE root FROM root WHERE (root[\"datetime\"] < \"2000-01-01T00:00:00.123456Z\") "
这就是为什么会产生不需要的结果的原因。这是一种奇怪的行为,我认为在那里可以做的不多。
您将字符串设为 string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234561Z"
,这样就可以正常工作了。
另一种方法是:
string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234560Z";
DateTime datetime = DateTime.Parse(dateTimeWithTrailingZero, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
var result= client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri("demoDb", "demoDocCollection"),"select * from c where c.datetime < '" + datetime.ToString("o") + "'").ToList();
解决方法是使用自定义 JsonConverter
。不幸的是,对于 DocumentClient
,在 DocumentClient
的构造函数中设置 JsonConverter
不会 起作用!转换器只有在全局(静态)JSON.NET 默认设置 (JsonConvert.DefaultSettings
) 中指定时才能正确拾取。
对于较新的CosmosClient
,在构造函数中设置自定义CosmosSerializer
是必要且足够的。要编写允许您指定自定义 JsonSerializerSettings
的自定义 CosmosSerializer
,您可以反编译内部 class CosmosJsonDotNetSerializer
并将其用作基础。
自定义 JsonConverter
看起来像这样:
/// <summary>
/// <see cref="JsonConverter" /> for Cosmos DB needed as long as the DateTime handling
/// problem has not been fixed.
/// </summary>
public class CosmosDbDateTimeJsonConverter : IsoDateTimeConverter
{
public CosmosDbDateTimeJsonConverter()
{
this.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
}
#region Overrides of JsonConverter
/// <inheritdoc />
public override bool CanRead => false;
#endregion
}
此问题与 Microsoft.Azure.DocumentDB.Core v2.11.2
中的 DocumentClient
有关。 (更新:该错误也存在于 Microsoft.Azure.Cosmos
。)
当查询包含带尾随零的 DateTime
值时,Cosmos DB 的 LINQ 提供程序中似乎存在错误。考虑以下代码:
string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234560Z"; // trailing zero will be truncated by LINQ provider :-(
DateTime datetime = DateTime.Parse(dateTimeWithTrailingZero, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
IQueryable<Dictionary<string, object>> query =
client.CreateDocumentQuery<Dictionary<string, object>>(collectionUri)
.Where(x => (DateTime) x["datetime"] <= datetime);
query
的结果包括 个文档,其中 属性 datetime
例如"2000-01-01T00:00:00.1234567Z"
(尽管 不应该 )。
query
的结果 不包括 文档,其中 datetime
是 "2000-01-01T00:00:00.1234560Z"
(即使它 应该).
有什么方法可以使用 DocumentClient
和 LINQ 正确查询 DateTime
属性吗? (我知道使用原始 SQL 是有效的——出于各种原因,我 必须 使用 LINQ/IQueryable
。)
LINQ 是 removing/truncating 'Z' 之前的最后一个“0”,因此最终查询的工作方式如下:
query":"SELECT VALUE root FROM root WHERE (root[\"datetime\"] < \"2000-01-01T00:00:00.123456Z\") "
这就是为什么会产生不需要的结果的原因。这是一种奇怪的行为,我认为在那里可以做的不多。
您将字符串设为 string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234561Z"
,这样就可以正常工作了。
另一种方法是:
string dateTimeWithTrailingZero = "2000-01-01T00:00:00.1234560Z";
DateTime datetime = DateTime.Parse(dateTimeWithTrailingZero, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
var result= client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri("demoDb", "demoDocCollection"),"select * from c where c.datetime < '" + datetime.ToString("o") + "'").ToList();
解决方法是使用自定义 JsonConverter
。不幸的是,对于 DocumentClient
,在 DocumentClient
的构造函数中设置 JsonConverter
不会 起作用!转换器只有在全局(静态)JSON.NET 默认设置 (JsonConvert.DefaultSettings
) 中指定时才能正确拾取。
对于较新的CosmosClient
,在构造函数中设置自定义CosmosSerializer
是必要且足够的。要编写允许您指定自定义 JsonSerializerSettings
的自定义 CosmosSerializer
,您可以反编译内部 class CosmosJsonDotNetSerializer
并将其用作基础。
自定义 JsonConverter
看起来像这样:
/// <summary>
/// <see cref="JsonConverter" /> for Cosmos DB needed as long as the DateTime handling
/// problem has not been fixed.
/// </summary>
public class CosmosDbDateTimeJsonConverter : IsoDateTimeConverter
{
public CosmosDbDateTimeJsonConverter()
{
this.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
}
#region Overrides of JsonConverter
/// <inheritdoc />
public override bool CanRead => false;
#endregion
}