如何使用ONET v4和.NET Core WebAPI筛选SQL Server DateTime

时间:2019-05-10 03:21:52

标签: asp.net-core odata asp.net-core-webapi odata-v4

我正在使用OData Query v4和SQL Server 2012运行一个简单的.NET Core WebApi应用程序。

这可行,但是速度非常慢:

GET /api-endpoint?$filter=date(MyDateTimeField) ge 2018-01-01&$top=100

上面的URL生成的SQL查询:

SELECT TOP 100 * FROM MyTable WHERE ((((DATEPART(year, [MyDateTimeField]) * 10000) + (DATEPART(month, [MyDateTimeField]) * 100)) + DATEPART(day, [MyDateTimeField])) >= (((2018 * 10000) + (1 * 100)) + 1))

当我尝试这样做时:

GET /api-endpoint?$filter=MyDateTimeField ge 2018-01-01T00:00:00.00Z&$top=100

它将生成以下SQL查询:

SELECT TOP 100 * FROM MyTable WHERE [MyDateTimeField] > '2018-01-01T00:00:00.0000000'

哪个返回此错误:

  

从字符转换日期和/或时间时转换失败   字符串。

OData查询语法将是什么以生成与此类似的SQL查询?

SELECT TOP 100 * FROM MyTable WHERE [MyDateTimeField] > '2018-01-01'

2 个答案:

答案 0 :(得分:0)

假设MyDateTimeField的字段是 datetime 而不是 datatime2 ,请用{列注释 MyDateTimeField 首先:

[Column(TypeName="datetime")]

要使用public class MyTable { // ... other props [Column(TypeName="datetime")] public DateTime MyDateTimeField {get;set;} } 进行查询,请 datetime cast

DateTimeOffset

生成的?$filter=MyDateTimeField ge cast(2018-01-01T00:00:00.00Z,Edm.DateTimeOffset) 类似于:

sql

请注意,上面的 SELECT ..., [$it].[MyDateTimeField], FROM [MyTable] AS [$it] WHERE [$it].[MyDateTimeField] >= '2018-01-01T08:00:00.000' datetime ,而不是 2018-01-01T08:00:00.000

演示的屏幕截图:

enter image description here

答案 1 :(得分:0)

淹没在自己的挫败中后,我终于找到了一个解决方案,该解决方案不会迫使我的api使用者将DateTime字符串转换为一个丑陋,冗长,令人不安的表达式。 我还希望我的模型透明地看起来像使用 DateTimeOffset 而不是 DateTime ,这将使我将来能够重构数据库并最终使用DateTimeOffset,即使到目前为止我仍然不这样做无需处理时区。 我的局限性是我无法更新旧数据库,因此解决方案就在这里。

这是您的解决方案吗?

此解决方案仅在以下情况下有用:

  • 您不能(或不想)更新数据库列的类型(如果可以的话,应该可以解决问题)
  • 您可以使用或更改您的实体以使用 DateTimeOffset 而不是 DateTime (如果是新的api,请考虑这样做以符合odata标准并允许您重构将来的参考底图数据库)
  • 您不需要处理不同的时区(可以做到这一点,但是您需要研究解决方案才能做到)
  • 您使用EF Core> = 2.1

解决方案

  1. 使用 DateTimeOffset
  2. 更新您的实体
public class MyEntity {
    [...]
    public DateTimeOffset CreatedDateTime { get; set; }
    public DateTimeOffset ModifiedDateTime { get; set; }
    [...]
}
  1. 创建一个ValueConverter
public static class ValueConvertes
{
    public static ValueConverter<DateTimeOffset, DateTime> DateTimeToDateTimeOffset =
        new ValueConverter<DateTimeOffset, DateTime>(
            model => model.DateTime, 
            store => DateTime.SpecifyKind(store, DateTimeKind.UTC));
}
  1. 更新模型映射
public void Configure(EntityTypeBuilder<QuestionQML> builder)            
{
    builder.ToTable("MyEntityTable");
    builder.Property(e => e.CreatedDateTime)
       .HasColumnName("CreatedDateTime") // On DB datetime Type
       .HasConversion(ValueConvertes.DateTimeToDateTimeOffset);
    builder.Property(e => e.ModifiedDateTime)
       .HasColumnName("ModifiedDateTime") // On DB datetime Type
       .HasConversion(ValueConvertes.DateTimeToDateTimeOffset);
    [...]          
}

这使您可以通过以下方式进行过滤:

  

?$ filter = CreatedDateTime gt 2010-01-25T02:13:40Z   ?$ filter = CreatedDateTime gt 2010-01-25T02:13:40.01234Z   ?$ filter = CreatedDateTime gt 2010-01-25

特别感谢chris-clark

编辑: 当数据库中存储的日期时间为UTC时,可以使用更正后的代码DateTimeKind.UTC;如果您将其存储在其他时区中,则需要将“种类”设置为所使用的时区,但这会更改日期时间在“时间”中显示的方式结果,显示的是英国时区,例如Z(GMT time)或+01:00(BST时间)。