Подтвердить что ты не робот

Как заставить Web API OData v4 использовать DateTime

У меня довольно большая модель данных, которую я хочу открыть с помощью OData Web API с использованием протокола OData V4.

Базовые данные хранятся в базе данных SQL Server 2012. В этой базе данных есть много столбцов DateTime.

Поскольку я подключал его, я получил сообщение об ошибке, что System.DateTime не поддерживается.

Итак, вот мой вопрос, что я могу сделать, чтобы мои столбцы DateTime отображались в фиде OData?

ПРИМЕЧАНИЕ. Я не могу вернуться и изменить все мои столбцы на столбцы DateTimeOffset.

Я попытался изменить тип столбца в Entity Framework edmx, но он дал мне эту ошибку:

Указание членства недействительно. Тип "Edm.DateTimeOffset [Nullable = False, DefaultValue =, Precision =]" члена MyPropertyHere в типе "MyProject.MyEntity" несовместим с "SqlServer.datetime [Nullable = False, DefaultValue =, Precision = 3] 'члена' MyColumnName 'в типе' MyDataModel.Store.MyEntity '.

(Bascially syas, что DateTime не совместим с DateTimeOffset.)

Действительно ли команда API Web OData просто не учитывала всех, кто должен использовать тип SQL Server DateTime?

Обновление. Я нашел обходные пути для этого, но они требуют обновления модели EF для их работы. Я бы предпочел не обновлять несколько сотен свойств индивидуально, если я могу избежать этого.

Обновление:. Эта проблема заставила меня понять, что есть серьезные недостатки в том, как Microsoft управляет своими продуктами OData. Есть много вопросов, но это один из самых вопиющих. В OData Web API отсутствуют огромные недостатки. Транзакции и порядок вставки, являющийся двумя из них. Эти два элемента (которые входят в спецификацию OData и находятся в службах данных WCF до того, как Microsoft их убил) имеют решающее значение для любой реальной системы.

Вместо того, чтобы помещать время в те критические места, где им не хватает функциональности, которая находится в спецификации OData, они решают потратить свое время на удаление функциональных возможностей, которые очень помогли многим разработчикам. Он олицетворяет плохое управление, чтобы уделить приоритетное внимание устранению рабочих функций при добавлении в крайне необходимые функции.

Я пробовал обсудить их с представителем Web API OData, и в конце концов, у меня открылась проблема/билет, который был закрыт через несколько дней. Это был конец того, что они хотели сделать.

Как я уже сказал, есть еще много проблем (не связанных с DateTime, поэтому я не буду перечислять их здесь) с управлением OData Web API. Я был очень сильным сторонником OData, но вопиющие проблемы с управлением веб-интерфейсом OData заставили меня и мою команду/компанию отказаться от нее.

К счастью, обычный Web API можно настроить для использования синтаксиса OData. Это больше подходит для настройки ваших контроллеров, но в конце концов это прекрасно работает. И он поддерживает DateTime. (И, похоже, есть руководство, которое может по крайней мере избегать принятия безумно плохих решений.)

4b9b3361

Ответ 1

До сих пор DateTime не является частью OASIS OData V4 standard, а Web API не поддерживает тип DateTime, поддерживая тип DateTimeOffset.

Однако команда OData теперь работает над поддержкой типа DataTime. Я ожидаю, что вы можете использовать тип DateTime в следующей версии Web API. Если вы не можете дождаться следующего выпуска, я написал пример, основанный на blog. Надеюсь, это может вам помочь. Спасибо.

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

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 как обычно.

Test

Customer Data in the DB

Payload

The customers payload

Ответ 2

Наконец, Web API OData v4 теперь поддерживает тип DateTime в версии 5.5. Получите последний пакет nuget и не забудьте установить это:

config.SetTimeZoneInfo(TimeZoneInfo.Utc);

иначе часовой пояс свойства dateTime будет рассматриваться как локальный часовой пояс.

Дополнительная информация: ASP.NET Web API for OData V4 Docs DateTime support

Ответ 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

... Я не знаю, нормально ли мне отправлять пакет NuGet, содержащий исправленную библиотеку Microsoft, надеюсь, вам будет проще просить прощения, чем разрешение.

Обратите внимание, что рабочий элемент, который официально восстанавливает возможность использования DateTime в OData 4, отслеживается здесь: https://aspnetwebstack.codeplex.com/workitem/2072 Пожалуйста, проголосуйте за вопрос, если вы хотите его увидеть; хотя похоже, что он запланирован на 5.4-бета, поэтому он должен наступить на днях.

Ответ 4

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

Ответ 5

Обходные пути и http://damienbod.wordpress.com/2014/06/16/web-api-and-odata-v4-crud-and-actions-part-3/, не работают. Они работают только в одном направлении, а это означает, что запрос odata datetimeoffset с командой фильтра терпит неудачу, потому что он не является частью модели.

Вы больше не можете фильтровать или сортировать по этим полям или получать эту ошибку

?

/Aas/Деятельность $топ = 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 $расширить = Case &? $Фильтр =% 28Reminder %20Ne %20null %20and %20IsComplete %20eq %20null% 29 & $верхний = 15 & $OrderBy = напоминание & $кол = истина

Напоминание и Iscomplete - это дата и данные из активности через AppUserActivities.

Это странно, что это работает. У кого-нибудь есть решение?

Я подключаюсь к MySQL. Не существует datetimeoffset.

И все удивляются, почему никто не хочет разрабатывать технологии Microsoft.

Ответ 6

К сожалению, https://aspnetwebstack.codeplex.com/SourceControl/network/forks/johncrim/datetimefixes fork, заданный crimbo doesn `t реальная поддержка DateTime.

Появится новый fork https://aspnetwebstack.codeplex.com/SourceControl/network/forks/kj/odata53datetime?branch=odata-v5. 3-rtm на основе RTM OData v5.3, где свойства DateTime в объектах и ​​сложных типах поддерживаются в ответах сервера, запросах клиента POST/PUT/PATCH, предложениях $orderby и $filters, параметрах функции и возвратах. Мы начинаем использовать его в производственном коде и будем поддерживать эту вилку, пока поддержка DateTime не вернется в будущих официальных выпусках.

Ответ 7

Так как я использую библиотеку для odata с angular, я исследовал ее там:

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. здесь)

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

Итак, это должно быть ваше "форматирование"

2015-07-28T10:23:00Z

Ответ 10

Проведя разочаровывающий день, пытаясь сделать это, я просто наткнулся на единственный способ заставить его работать. Мы хотели иметь возможность фильтровать диапазоны дат, используя OData и Web API 2.2, что не является необычным вариантом использования. Наши данные находятся в SQL Server и хранятся как DateTime, и мы используем EF в API.

Версии:

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

Флажок контроллера:

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, и теперь мы строим клиента, который, надеюсь, будет работать с этим требованием, чтобы придерживаться 'datetime' перед фактическим значением. Я смотрю на использование Simple.OData.Client в MVC-контроллере, но мы можем даже решить обратиться прямо к API с клиентской стороны JavaScript в зависимости от того, сколько рефакторинга нам нужно делать. Я также хотел бы, чтобы пользовательский интерфейс Swagger работал с использованием Swashbuckle.OData, но это тоже сложно. Как только я сделал столько, сколько у меня есть время, я опубликую обновление с полезной информацией для тех, кто будет следовать, поскольку мне было очень неприятно, что было так сложно узнать, как сделать что-то, что якобы простое требование.