From b3c9afc5f53baea5afc36d9811f64e0619c98e48 Mon Sep 17 00:00:00 2001 From: Laurents Meyer Date: Fri, 25 Oct 2019 15:49:39 +0200 Subject: [PATCH] Fix some Timestamp/RowVersion issues. Still depends on https://github.com/aspnet/EntityFrameworkCore/issues/18592 Addresses #792 --- .../Extensions/MySqlPropertyExtensions.cs | 25 ++++++++++++++++--- .../Migrations/MySqlMigrationsSqlGenerator.cs | 5 ++-- .../Internal/MySqlDateTimeTypeMapping.cs | 3 ++- .../Internal/MySqlTypeMappingSource.cs | 12 ++++++--- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/EFCore.MySql/Extensions/MySqlPropertyExtensions.cs b/src/EFCore.MySql/Extensions/MySqlPropertyExtensions.cs index f90d51ef6..c8f08b6dc 100644 --- a/src/EFCore.MySql/Extensions/MySqlPropertyExtensions.cs +++ b/src/EFCore.MySql/Extensions/MySqlPropertyExtensions.cs @@ -3,8 +3,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Pomelo.EntityFrameworkCore.MySql.Internal; using Pomelo.EntityFrameworkCore.MySql.Metadata.Internal; +using Pomelo.EntityFrameworkCore.MySql.Storage.Internal; namespace Pomelo.EntityFrameworkCore.MySql.Extensions { @@ -102,7 +105,11 @@ public static bool IsCompatibleIdentityColumn(IProperty property) { var type = property.ClrType; - return (type.IsInteger() || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(DateTimeOffset)) && !HasConverter(property); + return (type.IsInteger() + || type == typeof(decimal) + || type == typeof(DateTime) + || type == typeof(DateTimeOffset)) + && !HasConverter(property); } /// @@ -114,11 +121,21 @@ public static bool IsCompatibleComputedColumn(IProperty property) { var type = property.ClrType; - return (type == typeof(DateTime) || type == typeof(DateTimeOffset)) && !HasConverter(property); + // RowVersion uses byte[] and the BytesToDateTimeConverter. + return (type == typeof(DateTime) || type == typeof(DateTimeOffset)) && !HasConverter(property) + || type == typeof(byte[]) && !HasExternalConverter(property); } private static bool HasConverter(IProperty property) - => (property.FindTypeMapping()?.Converter - ?? property.GetValueConverter()) != null; + => GetConverter(property) != null; + + private static bool HasExternalConverter(IProperty property) + { + var converter = GetConverter(property); + return converter != null && !(converter is BytesToDateTimeConverter); + } + + private static ValueConverter GetConverter(IProperty property) + => property.FindTypeMapping()?.Converter ?? property.GetValueConverter(); } } diff --git a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs index 5ba0b1fff..b44c7ae03 100644 --- a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs +++ b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs @@ -561,11 +561,10 @@ protected override void Generate( ? Array.CreateInstance(clrType.GetElementType(), 0) : clrType.GetDefaultValue()); - var isRowVersion = property.ClrType == typeof(byte[]) + var isRowVersion = (property.ClrType == typeof(DateTime) || property.ClrType == typeof(byte[])) && property.IsConcurrencyToken && property.ValueGenerated == ValueGenerated.OnAddOrUpdate; - - + var addColumnOperation = new AddColumnOperation { Schema = operation.Schema, diff --git a/src/EFCore.MySql/Storage/Internal/MySqlDateTimeTypeMapping.cs b/src/EFCore.MySql/Storage/Internal/MySqlDateTimeTypeMapping.cs index b8d01857d..bedcb53e8 100644 --- a/src/EFCore.MySql/Storage/Internal/MySqlDateTimeTypeMapping.cs +++ b/src/EFCore.MySql/Storage/Internal/MySqlDateTimeTypeMapping.cs @@ -30,12 +30,13 @@ public class MySqlDateTimeTypeMapping : DateTimeTypeMapping /// public MySqlDateTimeTypeMapping( [NotNull] string storeType, + Type clrType, ValueConverter converter = null, ValueComparer comparer = null, int? precision = null) : this( new RelationalTypeMappingParameters( - new CoreTypeMappingParameters(typeof(DateTime), converter, comparer), + new CoreTypeMappingParameters(clrType, converter, comparer), storeType, precision == null ? StoreTypePostfix.None : StoreTypePostfix.Precision, System.Data.DbType.DateTime, diff --git a/src/EFCore.MySql/Storage/Internal/MySqlTypeMappingSource.cs b/src/EFCore.MySql/Storage/Internal/MySqlTypeMappingSource.cs index 4bab43d0c..36d3d1925 100644 --- a/src/EFCore.MySql/Storage/Internal/MySqlTypeMappingSource.cs +++ b/src/EFCore.MySql/Storage/Internal/MySqlTypeMappingSource.cs @@ -53,9 +53,9 @@ public class MySqlTypeMappingSource : RelationalTypeMappingSource // DateTime private readonly MySqlDateTypeMapping _date = new MySqlDateTypeMapping("date", DbType.Date); - private readonly MySqlDateTimeTypeMapping _dateTime6 = new MySqlDateTimeTypeMapping("datetime", precision: 6); - private readonly MySqlDateTimeTypeMapping _dateTime = new MySqlDateTimeTypeMapping("datetime"); - private readonly MySqlDateTimeTypeMapping _timeStamp6 = new MySqlDateTimeTypeMapping("timestamp", precision: 6); + private readonly MySqlDateTimeTypeMapping _dateTime6 = new MySqlDateTimeTypeMapping("datetime", typeof(DateTime), precision: 6); + private readonly MySqlDateTimeTypeMapping _dateTime = new MySqlDateTimeTypeMapping("datetime", typeof(DateTime)); + private readonly MySqlDateTimeTypeMapping _timeStamp6 = new MySqlDateTimeTypeMapping("timestamp", typeof(DateTime), precision: 6); private readonly MySqlDateTimeOffsetTypeMapping _dateTimeOffset6 = new MySqlDateTimeOffsetTypeMapping("datetime", precision: 6); private readonly MySqlDateTimeOffsetTypeMapping _dateTimeOffset = new MySqlDateTimeOffsetTypeMapping("datetime"); private readonly MySqlDateTimeOffsetTypeMapping _timeStampOffset6 = new MySqlDateTimeOffsetTypeMapping("timestamp", precision: 6); @@ -65,11 +65,13 @@ public class MySqlTypeMappingSource : RelationalTypeMappingSource private readonly RelationalTypeMapping _binaryRowVersion = new MySqlDateTimeTypeMapping( "timestamp", + typeof(byte[]), new BytesToDateTimeConverter(), new ByteArrayComparer()); private readonly RelationalTypeMapping _binaryRowVersion6 = new MySqlDateTimeTypeMapping( "timestamp", + typeof(byte[]), new BytesToDateTimeConverter(), new ByteArrayComparer(), precision: 6); @@ -426,7 +428,9 @@ private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingIn { if (mappingInfo.IsRowVersion == true) { - return _connectionInfo.ServerVersion.SupportsDateTime6 ? _binaryRowVersion6 : _binaryRowVersion; + return _connectionInfo.ServerVersion.SupportsDateTime6 + ? _binaryRowVersion6 + : _binaryRowVersion; } var size = mappingInfo.Size ??