Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

数据库表里所有时间字段,修改为utc时间 #159

Closed
wdydxf opened this issue Apr 30, 2020 · 3 comments
Closed

数据库表里所有时间字段,修改为utc时间 #159

wdydxf opened this issue Apr 30, 2020 · 3 comments
Labels
Feature 🔨 新功能,新特性 Finished ✔️ 实现并完工 Useful 💯 对于解决问题有帮助
Milestone

Comments

@wdydxf
Copy link

wdydxf commented Apr 30, 2020

您的功能请求与现有问题有关吗?请描述

数据库表的时间字段, 能否修改为UTC时间, 框架如果能统一处理的话就更好了

描述您想要的解决方案

  1. 数据库表的时间字段全部存储为utc时间.
  2. 返回给前端时, 自动转为当前登录用户的时区

描述你考虑过的替代方案

现在方案也能用, 但如果框架能覆盖该功能的话, 适用更加广泛

附加上下文,比如截图

@gmf520
Copy link
Member

gmf520 commented Apr 30, 2020

是否使用UTC时间还是应该按项目实际需要进行选择吧,我觉得框架武断引入会增加不少复杂度

@gmf520 gmf520 added Feature 🔨 新功能,新特性 Useful 💯 对于解决问题有帮助 labels Aug 26, 2020
@gmf520
Copy link
Member

gmf520 commented Aug 26, 2020

给实体的DateTime类型属性添加UTC存储支持

需求

  1. 数据库中的时间存储为 DateTimeKind.Utc 类型
  2. C#代码中的时间存取使用 DateTimeKind.Local 类型
  3. 由 EFCore 负责将C#代码中的Local时间转换为Utc时间存储到数据库,将从数据库取出的Utc时间转换为Local时间到C#代码中使用

EFCore中技术支持

在实体的EntityConfiguration中,对时间类型的属性添加转换器,如下:

    builder.Property(m => m.CreatedTime).HasConversion(local => local.ToUniversalTime(), utc => utc.ToLocalTime());

如此,每个实体的配置都要添加转换器,是件繁琐的事,可以通过DI注入解决

DI服务实现方式

1. 添加服务接口

namespace OSharp.Entity
{
    /// <summary>
    /// 实体时间属性UTC转换器
    /// </summary>
    public interface IEntityDateTimeUtcConversion
    {
        /// <summary>
        /// 转换指定的实体类型。
        /// </summary>
        /// <param name="entityType">实体类型</param>
        void Convert(IMutableEntityType entityType);
    }
}

2. 服务实现

namespace OSharp.Entity
{
    /// <summary>
    /// 实体时间属性UTC转换器
    /// </summary>
    /// <seealso cref="OSharp.Entity.IEntityDateTimeUtcConversion" />
    public class EntityDateTimeUtcConversion : IEntityDateTimeUtcConversion
    {
        private readonly ValueConverter<DateTime, DateTime> _dateTimeConverter;
        private readonly ValueConverter<DateTime?, DateTime?> _nullableDateTimeConverter;

        /// <summary>
        /// 初始化一个<see cref="EntityDateTimeUtcConversion"/>类型的新实例
        /// </summary>
        public EntityDateTimeUtcConversion()
        {
            _dateTimeConverter = new ValueConverter<DateTime, DateTime>(
                local => local.ToUniversalTime(),
                utc => utc.ToLocalTime());
            _nullableDateTimeConverter = new ValueConverter<DateTime?, DateTime?>(
                local => local.HasValue ? local.Value.ToUniversalTime() : local,
                utc => utc.HasValue ? utc.Value.ToLocalTime() : utc);
        }

        /// <summary>
        /// 转换指定的实体类型。
        /// </summary>
        /// <param name="entityType">实体类型</param>
        public void Convert(IMutableEntityType entityType)
        {
            foreach (IMutableProperty property in entityType.GetProperties())
            {
                if (property.ClrType == typeof(DateTime))
                {
                    property.SetValueConverter(_dateTimeConverter);
                }
                else if (property.ClrType == typeof(DateTime?))
                {
                    property.SetValueConverter(_nullableDateTimeConverter);
                }
            }
        }
    }
}

3. 添加服务

EntityFrameworkCorePackBase

services.TryAddSingleton<IEntityDateTimeUtcConversion, EntityDateTimeUtcConversion>();

4. 添加数据上下文配置

添加配置选项 DateTimeUtcFormatEnabled,作为上下文是否启用UTC时间存储的开关

"SqlServer": {
  "DbContextTypeName": "OSharp.Entity.DefaultDbContext,OSharp.EntityFrameworkCore",
  "ConnectionString": "Data Source=osharp-mvc-dev.db",
  "DatabaseType": "Sqlite",
  "LazyLoadingProxiesEnabled": true,
  "DateTimeUtcFormatEnabled": true,
  "AuditEntityEnabled": true,
  "AutoMigrationEnabled": true
}

5. 在 DbContextBase 的 OnModelCreating 应用服务

List<IMutableEntityType> entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
foreach (IMutableEntityType entityType in entityTypes)
{
    //启用时间属性UTC格式
    if (_osharpDbOptions.DateTimeUtcFormatEnabled)
    {
        IEntityDateTimeUtcConversion utcConversion = _serviceProvider.GetService<IEntityDateTimeUtcConversion>();
        utcConversion.Convert(entityType);
    }
    // ...
}

@gmf520 gmf520 added the Finished ✔️ 实现并完工 label Aug 26, 2020
@gmf520 gmf520 closed this as completed Aug 26, 2020
@gmf520 gmf520 added this to the vNext milestone Aug 26, 2020
@gmf520 gmf520 mentioned this issue Aug 27, 2020
8 tasks
@gmf520 gmf520 modified the milestones: v3.1.7, v5.0.4 Mar 17, 2021
@gmf520
Copy link
Member

gmf520 commented Mar 17, 2021

基于 #212 的改进,对此功能进行重构

添加接口IEntityBatchConfiguration的实现:

    /// <summary>
    /// 配置实体的时间属性的Utc时间转换,在数据库中保存Utc时间,在代码运行时使用当前时区的时间
    /// </summary>
    public class PropertyUtcDateTimeConfiguration : IEntityBatchConfiguration
    {
        private readonly IEntityManager _entityManager;
        private readonly OsharpOptions _osharpOptions;
        private readonly ValueConverter<DateTime, DateTime> _dateTimeConverter;
        private readonly ValueConverter<DateTime?, DateTime?> _nullableDateTimeConverter;

        /// <summary>
        /// 初始化一个<see cref="PropertyUtcDateTimeConfiguration"/>类型的新实例
        /// </summary>
        public PropertyUtcDateTimeConfiguration(IServiceProvider provider)
        {
            _entityManager = provider.GetService<IEntityManager>();
            _osharpOptions = provider.GetOSharpOptions();

            _dateTimeConverter = new ValueConverter<DateTime, DateTime>(
                local => local.ToUniversalTime(),
                utc => utc.ToLocalTime());
            _nullableDateTimeConverter = new ValueConverter<DateTime?, DateTime?>(
                local => local.HasValue ? local.Value.ToUniversalTime() : local,
                utc => utc.HasValue ? utc.Value.ToLocalTime() : utc);
        }

        /// <summary>
        /// 配置指定的<see cref="IMutableEntityType"/>
        /// </summary>
        /// <param name="modelBuilder">模型构建器</param>
        /// <param name="mutableEntityType">实体的<see cref="IMutableEntityType"/>类型</param>
        public void Configure(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType)
        {
            foreach (IMutableProperty property in mutableEntityType.GetProperties())
            {
                if (property.ClrType == typeof(DateTime))
                {
                    property.SetValueConverter(_dateTimeConverter);
                }
                else if (property.ClrType == typeof(DateTime?))
                {
                    property.SetValueConverter(_nullableDateTimeConverter);
                }
            }
        }
    }

使用配置开关不妥,移除配置开关

由于此功能涉及数据库中的数据统一性,使用配置来开关,有可能造成数据不一致的问题,是否启用此功能,应在代码层面固定,而不应能轻易在配置文件中更改,故而移除OSharpDbContextOptions中的DateTimeUtcFormatEnabled配置项

加入DI来启用功能

services.AddSingleton<IEntityBatchConfiguration, PropertyUtcDateTimeConfiguration>();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature 🔨 新功能,新特性 Finished ✔️ 实现并完工 Useful 💯 对于解决问题有帮助
Projects
None yet
Development

No branches or pull requests

2 participants