it-swarm.dev

Comment faire pour que Web API OData v4 utilise DateTime

Je souhaite exposer un modèle de données relativement volumineux à l'aide de la Web API OData à l'aide du protocole OData V4.

Les données sous-jacentes sont stockées dans une base de données SQL Server 2012. Cette base de données contient de nombreuses colonnes DateTime.

Au moment de le brancher, j'ai eu une erreur indiquant que System.DateTime n'était pas pris en charge.

Alors voici ma question, que puis-je faire pour que mes colonnes DateTime soient visibles dans le flux OData?

REMARQUE: je ne peux pas revenir en arrière et changer toutes mes colonnes en colonnes DateTimeOffset.

J'ai essayé de changer le type de la colonne dans Entity Framework edmx, mais cela m'a donné l'erreur suivante: 

La cartographie des membres spécifiée n'est pas valide. Le type 'Edm.DateTimeOffset [Nullable = False, DefaultValue =, Precision =]' du membre 'MyPropertyHere' dans le type 'MyProject.MyEntity' n'est pas compatible avec 'SqlServer.datetime [Nullable = False, DefaultValue =, Precision = 3] 'du membre' MyColumnName 'dans le type' MyDataModel.Store.MyEntity '.

(En gros, syas que DateTime n'est pas compatible avec DateTimeOffset.)

L’équipe OData de l’API Web a-t-elle vraiment laissé de côté tous ceux qui doivent utiliser le type DateTime de SQL Server?

Mise à jour: j'ai trouvé des solutions de contournement, mais ils ont besoin de mettre à jour le modèle EF pour fonctionner. Je préférerais ne pas avoir à mettre à jour plusieurs centaines de propriétés individuellement si je peux l'éviter.

Mise à jour: Ce problème m'a fait comprendre qu'il existe de profondes failles dans la manière dont Microsoft gère ses produits OData. Il y a beaucoup de problèmes, mais celui-ci est le plus criant. Il manque énormément de fonctionnalités dans l’API Web OData. _ {Transactions et commande d'inserts étant deux d'entre eux. Ces deux éléments (qui figurent dans la spécification OData et étaient dans WCF Data Services avant que Microsoft ne la tue) sont essentiels à tout système réel. 

Mais plutôt que de consacrer du temps à ces points critiques où il manque des fonctionnalités qui figurent dans la spécification OData, ils décident de passer leur temps à supprimer des fonctionnalités très utiles pour de nombreux développeurs . Il incarne la mauvaise gestion pour donner la priorité à la suppression des fonctionnalités opérationnelles par rapport à l'ajout de fonctionnalités indispensables.

J'ai essayé d'en discuter avec le représentant Web API OData et j'ai finalement ouvert un numéro/ticket qui a été fermé quelques jours plus tard. C'était la fin de ce qu'ils étaient prêts à faire.

Comme je l'ai dit, il y a beaucoup plus de problèmes (non liés à DateTime, je ne les énumérerai donc pas ici) avec la gestion de Web API OData. Je suis un fervent partisan d'OData, mais les problèmes criants posés par la Web API La direction d'OData nous ont obligés, ainsi qu'à mon équipe/entreprise, à l'abandonner.

Heureusement, une API Web normale peut être configurée pour utiliser la syntaxe OData. La configuration de vos contrôleurs nécessite plus de travail, mais elle fonctionne très bien à la fin. Et il prend en charge DateTime. (Et semble avoir une direction qui peut au moins éviter de prendre des décisions incroyablement mauvaises.)

26
Vaccano

Jusqu'ici, DateTime ne fait pas partie du standard OASIS OData V4 et l'API Web ne prend pas en charge le type DateTime, mais prend en charge le type DateTimeOffset. 

Cependant, l'équipe OData travaille actuellement à la prise en charge du type DataTime. Je suppose que vous pouvez utiliser le type DateTime dans la prochaine version de l'API Web. Si vous ne pouvez pas attendre la prochaine version, j’ai écrit un exemple basé sur le blog . J'espère que ça peut t'aider. Merci.

Modèle

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

Contexte DB

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

Manette

Ajoutez le contrôleur OData comme d'habitude.

Test

Customer Data in the DB

Charge utile

The customers payload

17
Sam Xu

Enfin, l’API Web OData v4 prend désormais en charge le type DateTime dans la version 5.5. Obtenez le dernier paquet de nuget et n'oubliez pas de régler ceci:

config.SetTimeZoneInfo(TimeZoneInfo.Utc);

sinon, le fuseau horaire de la propriété dateTime serait considéré comme un fuseau horaire local.

Plus d'infos: ASP.NET Web API for OData V4 Docs DateTime support

17

Une autre solution consiste à patcher le projet de manière à ce que DateTime soit autorisé à nouveau. C'est ce que j'ai fait. Vous pouvez obtenir le code ici si vous le souhaitez:

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

J'ai aussi poussé un paquet NuGet pour le rendre facile à utiliser, vous pouvez obtenir le paquet en utilisant les informations ici:

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

... Je ne sais pas si je peux poster un package NuGet contenant une bibliothèque Microsoft corrigée. J'espère qu'il sera plus facile de demander pardon que l'autorisation.

Notez que l'élément de travail permettant de restaurer officiellement la possibilité d'utiliser DateTime dans OData 4 est suivi ici: https://aspnetwebstack.codeplex.com/workitem/2072 Veuillez voter pour le problème si vous 'aimerais le voir; bien que cela semble être prévu pour la version 5.4-bêta, cela devrait arriver un de ces jours.

3
crimbo

Malheureusement, https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johncrim/datetimefixes fork, donné par crimbo n'a pas encore pris en charge DateTime.

Il y a le nouveau fork https://aspnetwebstack.codeplex.com/SourceControl/network/forks/kj/odata53datetime? Branch = odata-v5.3-rtm basé sur OData v5.3 RTM, où les propriétés DateTime dans les entités et les types complexes sont prises en charge dans les réponses du serveur, les demandes client POST/PUT/PATCH, les clauses $ orderby et $ filters, les paramètres de fonction et les retours. Nous commençons à l’utiliser dans le code de production et prenons en charge cette fourchette, jusqu’à ce que le support de DateTime revienne dans les prochaines versions officielles.

1

Cette solution de contournement et celle de http://damienbod.wordpress.com/2014/06/16/web-api-and-odata-v4-crud-and-actions-part-3/ , ni travail. Ils ne fonctionnent que dans un sens, ce qui signifie que l'interrogation de odata datetimeoffset à l'aide de la commande filter échoue car elle ne fait pas partie du modèle.

Vous ne pouvez plus filtrer ni trier ces champs ou obtenir cette erreur

/ Aas/Activités? $ Top = 11 & $ orderby = CreatedAt

Donne cette erreur:

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

Mais cela fonctionne comme il est adressé indirectement:

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

Rappel et Iscomplete sont des date et date de l'activité depuis AppUserActivities. 

C'est bizarre que cela fonctionne. Quelqu'un a-t-il une solution?

Je me connecte à MySQL. Il n'y a pas de datetimeoffset.

Et tout le monde se demande pourquoi personne ne veut développer les technologies Microsoft.

1
Jon Alberghini

Depuis que j'utilise une bibliothèque pour odata avec angular, je l'ai étudiée ici:

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

Là vous pouvez voir (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";
        }
    };

Et le test attend (cf. ici )

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

Donc, cela devrait être votre "mise en forme"

2015-07-28T10:23:00Z
1
NicoJuicy
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";
1
Ivan Petrov

Les deux suivants fonctionnent avec ODATA 4

  1. c’est la dernière mise à jour que je vois avec .Net 4.7 et Microsoft.Aspnet.ODATA 5.3.1

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

  2. cela fonctionne aussi, il doit être dans cette aaaa-mm-jjThh: mm: ss.ssZ

    $ filter = Dateoftravel 2018-02-02T00: 00: 00.00Z

0
Sundara Prabu

Installez les correctifs System.Web.OData 5.3.0-datetime de https://www.nuget.org/packages/Patches.System.Web.OData/

0
JeeShen Lee

Après avoir passé une journée frustrante à essayer de faire cela, je viens de trébucher sur le seul moyen de le faire fonctionner. Nous voulions pouvoir filtrer par plages de dates en utilisant OData et Web API 2.2, ce qui n’est pas un cas d’utilisation peu commun. Nos données sont dans SQL Server et sont stockées sous le format DateTime. Nous utilisons EF dans l'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

Extrait d'entité:

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

Extrait de contrôleur:

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

Voici le bit magique - voir le bas de cette page MSDN sous le titre "Référence à différents types de données dans des expressions de filtre" et vous trouverez une note disant:

Les valeurs DateTime doivent être délimitées par des guillemets simples et précédé de Word datetime, tel que datetime'2010-01-25T02: 13: 40.1374695Z '.

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

Jusqu'ici, nous travaillons dans Postman et nous construisons maintenant le client qui devrait, espérons-le, respecter cette exigence, à savoir coller la date et l'heure devant la valeur réelle. Je cherche à utiliser Simple.OData.Client dans un contrôleur MVC, mais nous pouvons même décider d'appeler directement l'API à partir de JavaScript côté client, en fonction du travail de refactorisation que nous devons effectuer. J'aimerais aussi que l'interface utilisateur de Swagger fonctionne avec Swashbuckle.OData, mais cela s'avère également délicat. Une fois que j'ai fait tout ce que j'ai eu le temps de faire, je posterai une mise à jour avec des informations utiles pour ceux qui suivront, car je trouvais très frustrant qu'il était si difficile de trouver comment faire quelque chose qui est apparemment une simple exigence. .

0
Steve Pettifer

Cela ressemble à un mappage DateTimeOffset <-> DateTime sera inclus dans Microsoft ASP.NET Web API 2.2 pour OData v4.0 5.4.0:

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

0
Craig Boland