【问题标题】:C# OdataV4 Microsoft.OData.Client.DataServiceContext handle TimezoneC# OdataV4 Microsoft.OData.Client.DataServiceContext 处理时区
【发布时间】:2019-10-22 10:48:56
【问题描述】:

如标题所述,我需要知道如何设置时区以在 OdataV4 客户端中使用。

在我们的数据库中,我们一直将 DateTime-Values 存储为 GMT+1。

很长一段时间以来,我们一直在使用在 OdataV3 上工作的 WebApi。 只要我们使用 OdataV3,我们就没有遇到过与 TimeZones 相关的问题。

在切换到 OdataV4 后,我们现在面临一些真实的、几乎令人瞩目的问题,即即使我们将服务器上的 TimeZone 设置为 GMT+1,客户端现在也将 DateTimes 转换为 UTC。

这是服务器的输出:

如您所见,时间是相同的。 +02:00 与夏令时有关!
现在无论出于何种原因,客户端在调试时都会显示此时间戳:

我试图找到一种方法,它告诉 DataServiceContext 不使用 UTC 但找不到任何方法。我得到的最接近的是this post,但它似乎不适用于我。

处理 DateTimes 的代码非常多,我们负担不起全部重构。 此外,将服务器切换回 UTC 也不是一种选择,因为那时必须调整每个应用程序。

问题

如何将DataServiceContext 或影响组件 (JsonSerializer f.e.) 设置为我选择的时区?

【问题讨论】:

  • 请用发送到客户端的 JSON (DTO) 对象的示例更新您的问题,不同时区是否有多个消费者。
  • 您是说希望客户端自动将所有 DateTimeOffsets 转换为特定时区的 DateTime。
  • @lok​​usking 你找到解决这个问题的方法了吗?
  • @Alex 不幸的是还没有。我开始考虑分叉 Odata-Repo 并自己编译它

标签: c# entity-framework asp.net-web-api2 odata


【解决方案1】:

我首先检查的是 服务器 上的 OData 配置设置为 UTC,以下是我在 OData v4 API 中使用的标准注册方法,我将保留其他条目在那里帮助您确定在管道中的哪个位置实现对SetTimeZoneInfo的调用

public static void Register(HttpConfiguration config)
{
    // To enable $select and $filter on all fields by default
    config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
    config.SetDefaultQuerySettings(new Microsoft.AspNet.OData.Query.DefaultQuerySettings() { EnableCount = true, EnableExpand = true, EnableFilter = true, EnableOrderBy = true, EnableSelect = true, MaxTop = null });

    config.AddODataQueryFilter(new EnableQueryAttribute());
    config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

    // Set the timezone to UTC
    config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);

    // Register the odata routes and other config
    ...
}

以上代码为所有未指定时区的 DateTime 指定 UTC。以下变体显示了如何设置其他时区:

config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);
config.SetTimeZoneInfo(System.TimeZoneInfo.Local);
config.SetTimeZoneInfo(System.TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));

如果您想直接影响序列化器,那么您需要注册自己的自定义ODataSerializerProvider,因为 OData v4 框架使用它来反序列化 http 请求。这可能涉及很多,因此请先尝试简单的选项。

【讨论】:

  • 感谢您的回复。您的解决方案正是我想要的。现在每个客户都必须努力使用 UTC。如果您不从事国际工作,这是无稽之谈。我基本上在寻找的是一种告诉客户不要将 DateTimes 反序列化为 UTC 的方法。
  • 我说明的是如何设置时区,对于没有将您的时区插入解决方案,我深表歉意,我已更新解决方案以突出显示如何设置特定时区。
  • 是的...无论如何感谢您的帮助。我仍在为客户端而不是服务器寻求解决方案......
【解决方案2】:

我认为这是一个或许可以解决您的问题的解决方法:

它不会覆盖反序列化,而是通过修改响应来修复日期。

响应默认反序列化为 UTC,这会将所有 DateTime 对象转换为本地时区(您可以修改代码以更改为您想要的任何时区)。我假设您的服务器和客户端位于同一时区。

您可以将服务器时区设置为您想要的任何值,但我认为它将是 +1(很可能是本地时区)。

    /// <inheritdoc />
    public class YourDataServiceContext: DataServiceContext
    {

        /// <inheritdoc />
        protected YourDataServiceContext(Uri uri) : base(uri)
        {
            //add any code if you need here
            Configurations.ResponsePipeline.OnEntityMaterialized(ConvertDatesToLocalZone);
        }

        private void ConvertDatesToLocalZone(MaterializedEntityArgs obj)
        {
            var entity = obj?.Entity;
            if (entity == null) return;
            var props = entity.GetType()
                .GetProperties()
                .Where(it =>
                    it.PropertyType == typeof(DateTime)
                    || it.PropertyType == typeof(DateTime?));

            foreach (var prop in props)
            {
                //get value and check if it isn't null
                var value = prop.GetValue(entity);
                if (!(value is DateTime oldValue)) continue;

                //check if property has setter
                var setMethod = prop.SetMethod;
                if (setMethod == null) continue;

                //convert to local time
                value = oldValue.ToLocalTime();

                //set the new value
                setMethod.Invoke(entity, new[] { value });
            }
        }
    }

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2021-07-19
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    相关资源
    最近更新 更多