How to get Web API OData v4 to use DateTime

前端 未结 12 1182
时光取名叫无心
时光取名叫无心 2020-12-02 14:50

I have a fairly large data model that I want to expose using Web API OData using the OData V4 protocol.

The underlying data is stored in a SQL Server 2012 database.

相关标签:
12条回答
  • 2020-12-02 15:16

    So far, DateTime is not the part of the OASIS OData V4 standard and Web API doesn't support the DateTime type while it do support the DateTimeOffset type.

    However, OData Team are working on supporting the DataTime type now. I'd expect you can use the DateTime type in the next Web API release. If you can't wait for the next release, I wrote an example based on the blog . Hope it can help you. Thanks.

    Model

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

    DB Context

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

    Controller

    Add the OData Controller as normal.

    Test

    Customer Data in the DB

    Payload

    The customers payload

    0 讨论(0)
  • 2020-12-02 15:18

    Having spent a frustrating day trying to do this very thing, I have just stumbled across the only way I could get it to work. We wanted to be able to filter by date ranges using OData and Web API 2.2, which isn't an uncommon use case. Our data is in SQL Server and is stored as DateTime and we're using EF in the API.

    Versions:

    • 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

    Entity Snippet:

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

    Here's the magic bit - see the bottom of this MSDN page under the heading 'Referencing Different Data Types in Filter Expressions' and you will find a note saying:

    DateTime values must be delimited by single quotation marks and preceded by the word datetime, such as datetime'2010-01-25T02:13:40.1374695Z'.

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

    So far we have this working in Postman and we're now building the client which should hopefully work with this requirement to stick 'datetime' in front of the actual value. I'm looking at using Simple.OData.Client in an MVC controller but we may even decide to call straight to the API from client side JavaScript depending on how much refactoring we have to do. I'd also like to get Swagger UI working using Swashbuckle.OData but this is also proving to be tricky. Once I've done as much as I have time to, I'll post an update with useful info for those who follow as I found it very frustrating that it was so hard to find out how to do something that is ostensibly a simple requirement.

    0 讨论(0)
  • 2020-12-02 15:20

    Unfortunatly, https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johncrim/datetimefixes fork, given by crimbo doesn`t realy support DateTime.

    There is the new fork https://aspnetwebstack.codeplex.com/SourceControl/network/forks/kj/odata53datetime?branch=odata-v5.3-rtm based on OData v5.3 RTM, where DateTime properties in entities and complex types are supported in server answers, client POST/PUT/PATCH requests, $orderby and $filters clauses, function parameters and returns. We start to use it in production code and will support this fork, until DateTime support will return in future official releases.

    0 讨论(0)
  • 2020-12-02 15:20

    For those who are using older versions of OData without Datetime support, you can simply use the .AddQueryOption extension to include the datetime filters manually...

    Example - If you had a query defined as follows:

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

    This would not give you the result you expect b/c the translated query would effectively be something like https://test.com/Entity?$filter=DateField eq 2020-02-24T00:00:00Z. When it gets to the server this wont execute the expected query b/c of the datetime offset.

    to get around this do the following:

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

    This will allow you to query against odata using datetime. Now, all you need to do is handle the POST, PUT, DELETE commands.

    To do this, simply ensure that you have the timezone information or the client offset serialized in the header of the request. Use this in the web api to adjust the dates accordingly. I generally will add an extension method that is used in a custom serializer to adjust the dates when the payload is deserialized.

    Alos, be sure to write such that the newer version of web api that handle timezone information properly will continue to function as expected...

    0 讨论(0)
  • 2020-12-02 15:22

    This workarounds and the one from http://damienbod.wordpress.com/2014/06/16/web-api-and-odata-v4-crud-and-actions-part-3/, neither work. They work one way only, meaning that quering odata datetimeoffset with the filter command fails because it is not part of the model.

    You can no longer filter or sort by these fields or get this error

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

    Gives this error:

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

    But this works as it is addressed indirectly:

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

    Reminder and Iscomplete are date and datetiem from activity through AppUserActivities.

    This is wierd that that works. Does anyone have a solution?

    I connect to MySQL. There is not a datetimeoffset.

    And everyone wonders why no one wants to develop for Microsoft technologies.

    0 讨论(0)
  • 2020-12-02 15:28

    Both the following work with ODATA 4

    1 : This is the Latest update I see with .Net 4.7 and Microsoft.Aspnet.ODATA 5.3.1

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

    2 : This also works , it needs to be in this yyyy-mm-ddThh:mm:ss.ssZ

    $filter=DateofTravel lt 2018-02-02T00:00:00.00Z
    
    0 讨论(0)
提交回复
热议问题