30

我有一个相当大的数据模型,我想使用 OData V4 协议使用 Web API OData 公开它。

基础数据存储在 SQL Server 2012 数据库中。该数据库中有许多 DateTime 列。

当我连接它时,我收到一个错误,即 System.DateTime 不受支持。

所以这是我的问题,我该怎么做才能在 OData 提要中看到我的 DateTime 列?

注意:我无法返回并将所有列更改为 DateTimeOffset 列。

我尝试更改实体框架 edmx 中列的类型,但它给了我这个错误:

指定的成员映射无效。'MyProject.MyEntity' 类型中成员 'MyPropertyHere' 的类型 'Edm.DateTimeOffset[Nullable=False,DefaultValue=,Precision=]' 与 'SqlServer.datetime[Nullable=False,DefaultValue=,Precision=3] 不兼容'MyDataModel.Store.MyEntity' 类型中的成员 'MyColumnName'。

(基本上认为 DateTime 与 DateTimeOffset 不兼容。)

Web API OData 团队真的忽略了所有需要使用 SQL Server 类型的人DateTime吗?

更新:我找到了解决方法,但它们需要更新 EF 模型才能工作。如果可以避免的话,我宁愿不必单独更新数百个属性。

更新:这个问题让我意识到微软管理其 OData 产品的方式存在严重缺陷。有很多问题,但这个是最明显的。Web API OData 中有大量缺失的功能。 插入的事务和排序是其中的两个。这两个项目(在 OData 规范中并且在微软杀死它之前在 WCF 数据服务中)对于任何实际系统都至关重要。

但是,他们没有将时间花在那些缺少 OData 规范中的功能的关键点上,而是决定花时间删除对许多开发人员非常有帮助的功能。它集中体现了糟糕的管理,优先考虑删除工作功能而不是添加急需的功能。

我尝试与 Web API OData 代表讨论这些问题,最后,我打开了一个问题/票证,几天后又关闭了。这就是他们愿意做的事情的结束。

正如我所说,Web API OData 的管理还有很多问题(与 DateTime 无关,因此我不会在此列出)。 我一直是 OData 的坚定支持者,但 Web API OData 管理的明显问题迫使我和我的团队/公司放弃它。

幸运的是,普通的 Web API 可以设置为使用 OData 语法。设置控制器需要做更多的工作,但最终效果很好。它支持日期时间。(而且似乎拥有至少可以避免做出疯狂错误决定的管理层。)

4

12 回答 12

20

Finally Web API OData v4 now supports DateTime type in release 5.5 . Get latest nuget package and don't forget setting this:

config.SetTimeZoneInfo(TimeZoneInfo.Utc);

otherwise the timezone of the dateTime property would be considered as local timezone.

More info: ASP.NET Web API for OData V4 Docs DateTime support

于 2015-05-26T07:25:11.190 回答
19

到目前为止,DateTime 不是OASIS OData V4 标准的一部分,Web API 不支持 DateTime 类型,但它支持 DateTimeOffset 类型。

但是,OData 团队现在正在努力支持 DataTime 类型。我希望您可以在下一个 Web API 版本中使用 DateTime 类型。如果你等不及下一个版本,我根据 博客写了一个例子。希望它可以帮助你。谢谢。

模型

public class Customer
{
    private DateTimeWrapper dtw;

    public int Id { get; set; }

    public string Name { get; set; }

    public DateTime Birthday
    {
        get { return dtw; }
        set { dtw = value; }
    }

    [NotMapped]
    public DateTimeOffset BirthdayOffset
    {
        get { return dtw; }
        set { dtw = value; }
    }
}

public class DateTimeWrapper
{
    public static implicit operator DateTimeOffset(DateTimeWrapper p)
    {
        return DateTime.SpecifyKind(p._dt, DateTimeKind.Utc);
    }

    public static implicit operator DateTimeWrapper(DateTimeOffset dto)
    {
        return new DateTimeWrapper(dto.DateTime);
    }

    public static implicit operator DateTime(DateTimeWrapper dtr)
    {
        return dtr._dt;
    }

    public static implicit operator DateTimeWrapper(DateTime dt)
    {
        return new DateTimeWrapper(dt);
    }

    protected DateTimeWrapper(DateTime dt)
    {
        _dt = dt;
    }

    private readonly DateTime _dt;
}

数据库上下文

public DbSet<Customer> Customers { get; set; }

模型

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

builder.EntitySet<Customer>("Customers");

var cu = builder.StructuralTypes.First(t => t.ClrType == typeof(Customer));
cu.AddProperty(typeof(Customer).GetProperty("BirthdayOffset"));
var customer = builder.EntityType<Customer>();

customer.Ignore(t => t.Birthday);
var model = builder.GetEdmModel();

config.MapODataServiceRoute("odata", "odata", model);

控制器

正常添加 OData 控制器。

测试

数据库中的客户数据

有效载荷

客户有效载荷

于 2014-08-10T09:12:13.713 回答
3

另一种解决方案是修补项目,以便DateTime再次允许 - 这就是我所做的,如果需要,您可以在此处获取代码:

https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johcrim/datetimefixes

我还推送了一个 NuGet 包以使其易于使用,您可以使用此处的信息获取包:

http://www.nuget.org/packages/Patches.System.Web.OData/5.3.0-datetimefixes

...我不知道我是否可以发布包含修补的 Microsoft 库的 NuGet 包,我希望请求宽恕比许可更容易。

请注意,在此处跟踪正式恢复在 OData 4 中使用 DateTime 的能力的工作项: https ://aspnetwebstack.codeplex.com/workitem/2072 如果您想查看该问题,请为该问题投票;虽然它看起来像是预定了 5.4-beta,所以它应该是这些日子之一。

于 2014-09-07T19:18:48.897 回答
1

不幸的是,crimbo给出的https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johcrim/datetimefixes分叉并不真正支持 DateTime。

有基于 OData v5.3 RTM的新分支https://aspnetwebstack.codeplex.com/SourceControl/network/forks/ kj /odata53datetime?branch=odata-v5.3-rtm ,其中实体和复杂类型中的 DateTime 属性在服务器应答、客户端 POST/PUT/PATCH 请求、$orderby 和 $filters 子句、函数参数和返回中支持。我们开始在生产代码中使用它并将支持这个分支,直到 DateTime 支持将在未来的官方版本中返回。

于 2014-11-28T16:22:59.293 回答
1

这种解决方法和来自 http://damienbod.wordpress.com/2014/06/16/web-api-and-odata-v4-crud-and-actions-part-3/的解决方法都不起作用。它们仅以一种方式工作,这意味着使用 filter 命令查询 odata datetimeoffset 会失败,因为它不是模型的一部分。

您无法再按这些字段过滤或排序,或收到此错误

/Aas/Activity?$top=11&$orderby=CreatedAt

给出这个错误:

"code":"","message":"An error has occurred.","innererror":{
  "message":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata.metadata=minimal'.","type":"System.InvalidOperationException","stacktrace":"","internalexception":{
    "message":"The specified type member 'CreatedAt' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.","type":"System.NotSupportedException","stacktrace":"   

但这有效,因为它是间接解决的:

/Aas/AppUsers%28102%29/AppUserActivities?$expand=Case&$filter=%28Reminder%20ne%20null%20and%20IsComplete%20eq%20null%29&$top=15&$orderby=Reminder&$count=true

Reminder 和 Iscomplete 是通过 AppUserActivities 进行的活动的日期和日期时间。

这很奇怪,这很有效。有没有人有办法解决吗?

我连接到 MySQL。没有日期时间偏移。

每个人都想知道为什么没有人愿意为 Microsoft 技术进行开发。

于 2014-11-17T23:23:22.863 回答
1

由于我使用带角度的 odata 库,因此我在那里对其进行了调查:

https://github.com/devnixs/ODataAngularResources/blob/master/src/odatavalue.js

在那里你可以看到(javascript)

var generateDate = function(date,isOdataV4){
        if(!isOdataV4){
            return "datetime'" + date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2) + "T" + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2)+':'+("0" + date.getSeconds()).slice(-2) + "'";
        }else{
            return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2) + "T" + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2)+':'+("0" + date.getSeconds()).slice(-2) + "Z";
        }
    };

并且测试期望(参见此处

 $httpBackend.expectGET("/user(1)?$filter=date eq 2015-07-28T10:23:00Z")

所以这应该是你的“格式”

2015-07-28T10:23:00Z
于 2015-10-19T23:04:57.850 回答
1

以下两者都适用于 ODATA 4

1:这是我看到的 .Net 4.7 和Microsoft.Aspnet.ODATA 5.3.1的最新更新

$filter=DateofTravel lt cast(2018-05-15T00:00:00.00Z,Edm.DateTimeOffset)

2:这个也行,需要在这个yyyy-mm-ddThh:mm:ss.ssZ

$filter=DateofTravel lt 2018-02-02T00:00:00.00Z
于 2018-02-06T06:45:47.920 回答
0

看起来映射DateTimeOffset <-> DateTime将包含在Microsoft ASP.NET Web API 2.2 for OData v4.0 5.4.0 中

https://github.com/OData/WebApi/commit/2717aec772fa2f69a2011e841ffdd385823ae822

于 2015-02-05T00:17:25.000 回答
0
public class Customer
{
    private DateTimeWrapper dtw;

    public int Id { get; set; }

    public string Name { get; set; }

    public DateTime Birthday
    {
       get { return dtw; }
       set { dtw = value; }
    }

    [NotMapped]
    public DateTimeOffset BirthdayOffset
    {
        get { return dtw; }
        set { dtw = value; }
    }
}


var customer = builder.EntityType<Customer>();
customer.Ignore(t => t.Birthday);
customer.Property(t => t.BirthdayOffset).Name = "Birthday";
于 2014-11-03T18:05:32.220 回答
0

对于那些使用不支持日期时间的旧版本 OData 的用户,您可以简单地使用 .AddQueryOption 扩展名来手动包含日期时间过滤器......

示例 - 如果您的查询定义如下:

var date1 = new DateTime();
var query = from a in service.Entity
            where a.DateField = date1
            select a;

这不会给您预期的结果 b/c 翻译后的查询实际上类似于https://test.com/Entity ?$filter=DateField eq 2020-02-24T00:00:00Z。当它到达服务器时,它不会执行日期时间偏移量的预期查询 b/c。

要解决此问题,请执行以下操作:

var date1 = new DateTime().ToString("yyyy-MM-dd");
var filters = "date(DateField) eq " + date1;
var query = from a in service.Entity.AddQueryOption("$filter", filters);

这将允许您使用日期时间查询 odata。现在,您需要做的就是处理 POST、PUT、DELETE 命令。

为此,只需确保您在请求的标头中序列化了时区信息或客户端偏移量。在 web api 中使用它来相应地调整日期。我通常会添加一个在自定义序列化程序中使用的扩展方法,以调整反序列化有效负载的日期。

Alos,请务必编写以便正确处理时区信息的较新版本的 web api 将继续按预期运行......

于 2020-02-24T14:17:36.670 回答
0

从https://www.nuget.org/packages/Patches.System.Web.OData/安装 System.Web.OData 5.3.0-datetimefixes

于 2015-07-12T05:47:11.667 回答
0

花了令人沮丧的一天试图做这件事,我偶然发现了唯一能让它工作的方法。我们希望能够使用 OData 和 Web API 2.2 按日期范围进行过滤,这不是一个不常见的用例。我们的数据在 SQL Server 中,并存储为 DateTime,我们在 API 中使用 EF。

版本:

  • Microsoft.AspNet.WebApi.OData 5.7.0
  • Microsoft.AspNet.Odata 5.9.0
  • Microsoft.OData.Core 6.15.0
  • Microsoft.OData.Edm 6.15.0
  • Microsoft.Data.OData 5.7.0

实体片段:

[Table("MyTable")]
public class CatalogueEntry
{
    [Key]
    public Guid ContentId { get; set; }
    [StringLength(15)]
    public string ProductName { get; set; }
    public int EditionNumber { get; set; }
    public string Purpose { get; set; }
    public DateTime EditionDate { get; set; }
}

WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapODataServiceRoute("ProductCatalogue", "odata", GetImplicitEdm());

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Filters.Add(new UnhandledExceptionFilter());

        var includeVersionHeaders = ConfigurationManager.AppSettings["IncludeVersionHeaders"];
        if (includeVersionHeaders != null && bool.Parse(includeVersionHeaders))
        {
            config.Filters.Add(new BuildVersionHeadersFilter());
        }

        config.SetTimeZoneInfo(TimeZoneInfo.Utc);
    }

    private static IEdmModel GetImplicitEdm()
    {
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<CatalogueEntry>("ProductCatalogue");
        return builder.GetEdmModel();
    }
}

控制器片段:

public class ProductCatalogueController : EntitySetController<CatalogueEntry, string>
{
    [EnableQuery]
    public override IQueryable<CatalogueEntry> Get()
    {
        return _productCatalogueManager.GetCatalogue().AsQueryable();
    }

    protected override CatalogueEntry GetEntityByKey(string key)
    {
        return _productCatalogueManager.GetCatalogue().FirstOrDefault(c => c.ContentId.ToString() == key);
    }
}

这是神奇的一点 - 请参阅此 MSDN 页面底部的“在过滤器表达式中引用不同的数据类型”标题下,您会发现一条注释说:

DateTime 值必须用单引号分隔并以单词datetime开头,例如 datetime'2010-01-25T02:13:40.1374695Z'

http://localhost/Product-Catalogue/odata/ProductCatalogue?$filter=EditionDate lt datetime'2014-05-15T00:00:00.00Z'

到目前为止,我们已经在 Postman 中进行了这项工作,我们现在正在构建客户端,希望它能够满足这一要求,将“日期时间”放在实际值之前。我正在考虑在 MVC 控制器中使用 Simple.OData.Client,但我们甚至可能决定从客户端 JavaScript 直接调用 API,具体取决于我们必须进行多少重构。我还想让 Swagger UI 使用 Swashbuckle.OData 工作,但这也被证明很棘手。一旦我完成了我有时间做的事情,我会为那些关注的人发布一个包含有用信息的更新,因为我发现很难找到如何做表面上一个简单的要求的事情非常令人沮丧.

于 2016-04-07T14:25:06.543 回答