diff --git a/src/EFCore.Jet/Infrastructure/Internal/IJetOptions.cs b/src/EFCore.Jet/Infrastructure/Internal/IJetOptions.cs index 9a004df..4bd980c 100644 --- a/src/EFCore.Jet/Infrastructure/Internal/IJetOptions.cs +++ b/src/EFCore.Jet/Infrastructure/Internal/IJetOptions.cs @@ -16,5 +16,13 @@ namespace EntityFrameworkCore.Jet.Infrastructure.Internal bool UseOuterSelectSkipEmulationViaDataReader { get; } bool EnableMillisecondsSupport { get; } bool UseShortTextForSystemString { get; } + DateTimeOffsetType DateTimeOffsetType { get; } + } + + public enum DateTimeOffsetType + { + SaveAsString = 0, + SaveAsDateTime = 1, + SaveAsDateTimeUtc = 2 } } diff --git a/src/EFCore.Jet/Infrastructure/Internal/JetOptionsExtension.cs b/src/EFCore.Jet/Infrastructure/Internal/JetOptionsExtension.cs index 9514418..6467954 100644 --- a/src/EFCore.Jet/Infrastructure/Internal/JetOptionsExtension.cs +++ b/src/EFCore.Jet/Infrastructure/Internal/JetOptionsExtension.cs @@ -25,6 +25,7 @@ namespace EntityFrameworkCore.Jet.Infrastructure.Internal private bool _useOuterSelectSkipEmulationViaDataReader; private bool _enableMillisecondsSupport; private bool _useShortTextForSystemString; + private DateTimeOffsetType _dateTimeOffsetType = DateTimeOffsetType.SaveAsString; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -189,6 +190,23 @@ namespace EntityFrameworkCore.Jet.Infrastructure.Internal return clone; } + public virtual DateTimeOffsetType DateTimeOffsetType => _dateTimeOffsetType; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual JetOptionsExtension WithUseDateTimeOffsetType(DateTimeOffsetType dateTimeOffsetType = DateTimeOffsetType.SaveAsString) + { + var clone = (JetOptionsExtension)Clone(); + + clone._dateTimeOffsetType = dateTimeOffsetType; + + return clone; + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -287,6 +305,8 @@ namespace EntityFrameworkCore.Jet.Infrastructure.Internal = Extension._enableMillisecondsSupport.GetHashCode().ToString(CultureInfo.InvariantCulture); debugInfo["Jet:" + nameof(JetDbContextOptionsBuilder.UseShortTextForSystemString)] = Extension._useShortTextForSystemString.GetHashCode().ToString(CultureInfo.InvariantCulture); + debugInfo["Jet:" + nameof(JetDbContextOptionsBuilder.UseDateTimeOffsetType)] + = Extension._dateTimeOffsetType.GetHashCode().ToString(CultureInfo.InvariantCulture); } } } diff --git a/src/EFCore.Jet/Infrastructure/JetDbContextOptionsBuilder.cs b/src/EFCore.Jet/Infrastructure/JetDbContextOptionsBuilder.cs index 949d727..df13067 100644 --- a/src/EFCore.Jet/Infrastructure/JetDbContextOptionsBuilder.cs +++ b/src/EFCore.Jet/Infrastructure/JetDbContextOptionsBuilder.cs @@ -66,6 +66,9 @@ namespace EntityFrameworkCore.Jet.Infrastructure public virtual JetDbContextOptionsBuilder UseShortTextForSystemString(bool enabled = true) => WithOption(e => e.WithUseShortTextForSystemString(enabled)); + public virtual JetDbContextOptionsBuilder UseDateTimeOffsetType(DateTimeOffsetType dateTimeOffsetType = DateTimeOffsetType.SaveAsString) + => WithOption(e => e.WithUseDateTimeOffsetType(dateTimeOffsetType)); + /// /// Configures the context to use the default retrying . /// diff --git a/src/EFCore.Jet/Internal/JetOptions.cs b/src/EFCore.Jet/Internal/JetOptions.cs index 4dbf98b..53d2f0f 100644 --- a/src/EFCore.Jet/Internal/JetOptions.cs +++ b/src/EFCore.Jet/Internal/JetOptions.cs @@ -31,6 +31,7 @@ namespace EntityFrameworkCore.Jet.Internal EnableMillisecondsSupport = jetOptions.EnableMillisecondsSupport; ConnectionString = jetOptions.Connection?.ConnectionString ?? jetOptions.ConnectionString!; UseShortTextForSystemString = jetOptions.UseShortTextForSystemString; + DateTimeOffsetType = jetOptions.DateTimeOffsetType; } /// @@ -81,6 +82,13 @@ namespace EntityFrameworkCore.Jet.Internal nameof(JetOptionsExtension.UseShortTextForSystemString), nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); } + if (DateTimeOffsetType != jetOptions.DateTimeOffsetType) + { + throw new InvalidOperationException( + CoreStrings.SingletonOptionChanged( + nameof(JetOptionsExtension.DateTimeOffsetType), + nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); + } } private static DataAccessProviderType GetDataAccessProviderTypeFromOptions(JetOptionsExtension jetOptions) @@ -159,5 +167,7 @@ namespace EntityFrameworkCore.Jet.Internal /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual string? ConnectionString { get; private set; } + + public DateTimeOffsetType DateTimeOffsetType { get; private set; } } } diff --git a/src/EFCore.Jet/Storage/Internal/JetDateTimeOffsetTypeMapping.cs b/src/EFCore.Jet/Storage/Internal/JetDateTimeOffsetTypeMapping.cs index cbf1303..5643960 100644 --- a/src/EFCore.Jet/Storage/Internal/JetDateTimeOffsetTypeMapping.cs +++ b/src/EFCore.Jet/Storage/Internal/JetDateTimeOffsetTypeMapping.cs @@ -2,6 +2,7 @@ using System; using System.Data.Common; +using System.Globalization; using EntityFrameworkCore.Jet.Infrastructure.Internal; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Storage; @@ -12,13 +13,12 @@ namespace EntityFrameworkCore.Jet.Storage.Internal { private readonly IJetOptions _options; private const string DateTimeOffsetFormatConst = @"'{0:yyyy-MM-ddTHH:mm:ss.fffffffzzz}'"; + private const string DateTimeFormatConst = @"'{0:yyyy-MM-dd HH:mm:ss}'"; public JetDateTimeOffsetTypeMapping( [NotNull] string storeType, [NotNull] IJetOptions options) : base( - storeType, System.Data.DbType.String) // delibrately use DbType.DateTime, because OleDb will throw a - // "No mapping exists from DbType DateTimeOffset to a known OleDbType." - // exception when using DbType.DateTimeOffset. + storeType, options.DateTimeOffsetType == DateTimeOffsetType.SaveAsString ? System.Data.DbType.String : System.Data.DbType.DateTime) { _options = options; } @@ -37,8 +37,21 @@ namespace EntityFrameworkCore.Jet.Storage.Internal // OLE DB can't handle the DateTimeOffset type. if (parameter.Value is DateTimeOffset dateTimeOffset) { - parameter.Value = dateTimeOffset.ToString("O"); - parameter.DbType = System.Data.DbType.String; + switch (_options.DateTimeOffsetType) + { + case DateTimeOffsetType.SaveAsString: + parameter.Value = dateTimeOffset.ToString("O"); + parameter.DbType = System.Data.DbType.String; + break; + case DateTimeOffsetType.SaveAsDateTime: + parameter.Value = dateTimeOffset.DateTime; + parameter.DbType = System.Data.DbType.DateTime; + break; + case DateTimeOffsetType.SaveAsDateTimeUtc: + parameter.Value = dateTimeOffset.UtcDateTime; + parameter.DbType = System.Data.DbType.DateTime; + break; + } } base.ConfigureParameter(parameter); @@ -46,5 +59,27 @@ namespace EntityFrameworkCore.Jet.Storage.Internal protected override string SqlLiteralFormatString => DateTimeOffsetFormatConst; + + protected override string GenerateNonNullSqlLiteral(object value) + { + if (value is not DateTimeOffset offset) return base.GenerateNonNullSqlLiteral(value); + switch (_options.DateTimeOffsetType) + { + case DateTimeOffsetType.SaveAsString: + return base.GenerateNonNullSqlLiteral(offset); + case DateTimeOffsetType.SaveAsDateTime: + { + var dateTime = offset.DateTime; + return string.Format(CultureInfo.InvariantCulture, DateTimeFormatConst, dateTime); + } + case DateTimeOffsetType.SaveAsDateTimeUtc: + { + var dateTimeUtc = offset.DateTime; + return string.Format(CultureInfo.InvariantCulture, DateTimeFormatConst, dateTimeUtc); + } + default: + return ""; + } + } } } \ No newline at end of file diff --git a/src/EFCore.Jet/Storage/Internal/JetTypeMappingSource.cs b/src/EFCore.Jet/Storage/Internal/JetTypeMappingSource.cs index 7a0a11b..03a6cfd 100644 --- a/src/EFCore.Jet/Storage/Internal/JetTypeMappingSource.cs +++ b/src/EFCore.Jet/Storage/Internal/JetTypeMappingSource.cs @@ -92,7 +92,7 @@ namespace EntityFrameworkCore.Jet.Storage.Internal _datetime = new JetDateTimeTypeMapping("datetime", options, dbType: DbType.DateTime); _dateasdatetime = new JetDateTimeTypeMapping("date", options, dbType: DbType.Date); - _datetimeoffset = new JetDateTimeOffsetTypeMapping("varchar(50)", options); + _datetimeoffset = new JetDateTimeOffsetTypeMapping(options.DateTimeOffsetType == DateTimeOffsetType.SaveAsString ? "varchar(50)" : "datetime", options); _dateonly = new JetDateOnlyTypeMapping("date", options, dbType: DbType.Date); _timeonly = new JetTimeOnlyTypeMapping("time", options); _timespan = new JetTimeSpanTypeMapping("datetime", options);