如何使用Web API OData v4来使用DateTime

时间:2014-08-07 18:27:12

标签: .net odata asp.net-web-api asp.net-web-api-odata odata-v4

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

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

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

所以这是我的问题,我该如何才能在OData Feed中看到我的DateTime列?

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

我尝试更改Entity Framework edmx中列的类型,但它给了我这个错误:

  

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

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

Web API OData团队真的只是遗漏了需要使用SQL Server类型DateTime的所有人吗?

更新:我找到了相关的解决方法,但是他们需要更新EF模型才能使用它们。如果我能避免的话,我宁愿不必单独更新几百个属性。

  

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

     

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

     

我尝试与Web API OData代表讨论这些问题,最后,我打开了一个问题/故障单,几天后关闭了。那是他们愿意做的结束。

     

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

     

幸运的是,可以设置普通的Web API以使用OData语法。设置控制器的工作量更大,但最终工作得很好。它支持DateTime。 (并且似乎管理层至少可以远离制定疯狂的糟糕决策。)

12 个答案:

答案 0 :(得分:17)

到目前为止,DateTime不是OASIS OData V4 standard的一部分,并且Web API在支持DateTimeOffset类型时不支持DateTime类型。

但是,OData团队正在努力支持DataTime类型。我希望您可以在下一个Web API版本中使用DateTime类型。如果你不能等待下一个版本,我写了一个基于的例子 blog。希望它可以帮到你。感谢。

<强>模型

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; }

<强> EdmModel

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控制器。

<强>测试

Customer Data in the DB

<强>有效载荷

The customers payload

答案 1 :(得分:17)

最后,Web API OData v4现在支持版本5.5中的DateTime类型。获取最新的nuget包,不要忘记设置:

config.SetTimeZoneInfo(TimeZoneInfo.Utc);

否则dateTime属性的时区将被视为本地时区。

更多信息:ASP.NET Web API for OData V4 Docs DateTime support

答案 2 :(得分:3)

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

https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johncrim/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,但它应该是最近的一天。

答案 3 :(得分:1)

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";

答案 4 :(得分:1)

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

您无法再对这些字段进行过滤或排序,也不会出现此错误

  ?

/原子吸收光谱/活动$顶部= 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":"   

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

  

/原子吸收光谱/ AppUsers%28102%29 / AppUserActivities $扩大=案例&安培; $滤波器=%28Reminder%20NE%20null%20于是%20IsComplete%20当量%20null%29&安培; $顶部= 15&安培; $的OrderBy =提醒&安培; $计数=真

提醒和未完成是从活动到AppUserActivities的日期和日期。

这很有意义。有没有人有解决方案?

我连接到MySQL。没有datetimeoffset。

每个人都想知道为什么没有人愿意为微软技术开发。

答案 5 :(得分:1)

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

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

答案 6 :(得分: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";
        }
    };

测试期望(cfr。here

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

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

2015-07-28T10:23:00Z

答案 7 :(得分:0)

看起来像是映射 DateTimeOffset &lt; - &gt; DateTime 将包含在 Microsoft ASP.NET Web API 2.2 for OData v4.0 5.4.0 中:

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

答案 8 :(得分:0)

https://www.nuget.org/packages/Patches.System.Web.OData/

安装System.Web.OData 5.3.0-datetimefixes

答案 9 :(得分: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();
    }
}

Controller Snippet:

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);
    }
}

这是神奇的位 - 请参阅“在过滤器表达式中引用不同数据类型”标题下的this MSDN page底部,您会看到一条说明:

  

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工作,我们现在正在构建客户端,希望能够满足这一要求,将'datetime'粘贴在实际值之前。我正在寻找在MVC控制器中使用Simple.OData.Client,但我们甚至可能决定从客户端JavaScript直接调用API,具体取决于我们需要做多少重构。我也想让Swagger UI使用Swashbuckle.OData工作,但这也证明是棘手的。一旦我完成了尽可能多的时间,我会发布一个包含有用信息的更新,因为我发现非常令人沮丧的是很难找到如何做一些表面上看似简单要求的事情

答案 10 :(得分:0)

以下两项均适用于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

答案 11 :(得分:0)

对于那些使用不带Datetime支持的较早版本的OData的用户,只需使用.AddQueryOption扩展名即可手动包括datetime过滤器...

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

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);

这将允许您使用datetime查询odata。 现在,您所需要做的就是处理POST,PUT,DELETE命令。

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

Alos,请确保编写使正确处理时区信息的Web API的新版本能够继续按预期运行...