From 293fe7ed0eaf7899b7f11580ee99640042734c60 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 10 Nov 2025 14:12:10 +0300 Subject: [PATCH 01/11] bug EFCore: Error when saving an entity without specifying a default value --- .../Conventions/YdbConventionSetBuilder.cs | 1 + .../YdbValueGenerationConvention.cs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs index f4f5b6c8..922aa984 100644 --- a/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs +++ b/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs @@ -13,6 +13,7 @@ public override ConventionSet CreateConventionSet() { var coreConventions = base.CreateConventionSet(); coreConventions.Add(new YdbStringAttributeConvention(Dependencies)); + coreConventions.Add(new YdbValueGenerationConvention(Dependencies, RelationalDependencies)); return coreConventions; } } diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs new file mode 100644 index 00000000..2891b256 --- /dev/null +++ b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace EntityFrameworkCore.Ydb.Metadata.Conventions; + +public class YdbValueGenerationConvention( + ProviderConventionSetBuilderDependencies dependencies, + RelationalConventionSetBuilderDependencies relationalDependencies) + : RelationalValueGenerationConvention(dependencies, relationalDependencies) +{ + protected override ValueGenerated? GetValueGenerated(IConventionProperty property) + { + if (property.DeclaringType.IsMappedToJson() +#pragma warning disable EF1001 // Internal EF Core API usage. + && property.IsOrdinalKeyProperty() +#pragma warning restore EF1001 // Internal EF Core API usage. + && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false) + { + return ValueGenerated.OnAdd; + } + + var declaringTable = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault(); + if (declaringTable.Name == null) + { + return null; + } + + return property.GetComputedColumnSql(declaringTable) != null + ? ValueGenerated.OnAddOrUpdate + : property.GetDefaultValueSql(declaringTable) != null + ? ValueGenerated.OnAdd + : null; + } +} From f4d5edaa8ceaabd09cfef8274a3eb1bdaa4bf48c Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 10 Nov 2025 14:30:30 +0300 Subject: [PATCH 02/11] fix --- .../YdbValueGenerationConvention.cs | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs index 2891b256..f937e40b 100644 --- a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs +++ b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs @@ -14,25 +14,32 @@ public class YdbValueGenerationConvention( { protected override ValueGenerated? GetValueGenerated(IConventionProperty property) { - if (property.DeclaringType.IsMappedToJson() -#pragma warning disable EF1001 // Internal EF Core API usage. - && property.IsOrdinalKeyProperty() -#pragma warning restore EF1001 // Internal EF Core API usage. - && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false) - { - return ValueGenerated.OnAdd; - } + var table = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault(); - var declaringTable = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault(); - if (declaringTable.Name == null) - { - return null; - } + return !MappingStrategyAllowsValueGeneration(property, property.DeclaringType.GetMappingStrategy()) + ? null + : table.Name != null + ? GetValueGenerated(property, table) + : property.DeclaringType.IsMappedToJson() + && property.IsOrdinalKeyProperty() + && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false + ? ValueGenerated.OnAddOrUpdate + : property.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure).Any() + ? GetValueGenerated((IReadOnlyProperty)property) + : null; + } - return property.GetComputedColumnSql(declaringTable) != null - ? ValueGenerated.OnAddOrUpdate - : property.GetDefaultValueSql(declaringTable) != null - ? ValueGenerated.OnAdd - : null; + private new static ValueGenerated? GetValueGenerated( + IReadOnlyProperty property, + in StoreObjectIdentifier storeObject + ) + { + var valueGenerated = GetValueGenerated(property); + return valueGenerated + ?? (property.GetComputedColumnSql(storeObject) != null + ? ValueGenerated.OnAddOrUpdate + : property.GetDefaultValueSql(storeObject) != null + ? ValueGenerated.OnAdd + : null); } } From 021270b159a27ed83d5afc566d482ba215de660e Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Mon, 10 Nov 2025 14:59:13 +0300 Subject: [PATCH 03/11] fix linter --- .../src/Metadata/Conventions/YdbValueGenerationConvention.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs index f937e40b..4790cbb9 100644 --- a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs +++ b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs @@ -21,7 +21,9 @@ public class YdbValueGenerationConvention( : table.Name != null ? GetValueGenerated(property, table) : property.DeclaringType.IsMappedToJson() +#pragma warning disable EF1001 // Internal EF Core API usage. && property.IsOrdinalKeyProperty() +#pragma warning restore EF1001 // Internal EF Core API usage. && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false ? ValueGenerated.OnAddOrUpdate : property.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure).Any() From 550d80f0e7f9e2537d707a3f14c4e68cd132d941 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 11 Nov 2025 14:54:53 +0300 Subject: [PATCH 04/11] updates --- src/EFCore.Ydb/CHANGELOG.md | 1 + .../Conventions/YdbConventionSetBuilder.cs | 1 - .../YdbValueGenerationConvention.cs | 47 ----- .../Internal/YdbHistoryRepository.cs | 24 +-- .../Migrations/YdbMigrationsSqlGenerator.cs | 5 + .../Mapping/YdbDateOnlyTypeMapping.cs | 29 +-- .../Mapping/YdbDateTimeTypeMapping.cs | 30 ++-- .../Mapping/YdbTimeSpanTypeMapping.cs | 23 +++ .../Internal/Mapping/YdbTypeMapping.cs | 41 +++++ .../Storage/Internal/YdbTypeMappingSource.cs | 40 +++-- .../InsertWithDefaultsTests.cs | 168 ++++++++++++++++++ .../SqlQueryCollectionParameterTests.cs | 12 ++ src/Ydb.Sdk/CHANGELOG.md | 2 + src/Ydb.Sdk/src/Ado/YdbDataReader.cs | 7 + .../Ydb.Sdk.Ado.Tests/YdbParameterTests.cs | 9 + 15 files changed, 328 insertions(+), 111 deletions(-) delete mode 100644 src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs create mode 100644 src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTimeSpanTypeMapping.cs create mode 100644 src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs create mode 100644 src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs diff --git a/src/EFCore.Ydb/CHANGELOG.md b/src/EFCore.Ydb/CHANGELOG.md index 4966623c..74748791 100644 --- a/src/EFCore.Ydb/CHANGELOG.md +++ b/src/EFCore.Ydb/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fixed bug: Error when saving an entity without specifying a default value () - Fixed bug: SqlQuery throws exception when using list parameters ([#540](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/540)). - Added support for the YDB retry policy (ADO.NET) and new configuration methods in `YdbDbContextOptionsBuilder`: - `EnableRetryIdempotence()`: enables retries for errors classified as idempotent. You must ensure the operation itself is idempotent. diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs index 922aa984..f4f5b6c8 100644 --- a/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs +++ b/src/EFCore.Ydb/src/Metadata/Conventions/YdbConventionSetBuilder.cs @@ -13,7 +13,6 @@ public override ConventionSet CreateConventionSet() { var coreConventions = base.CreateConventionSet(); coreConventions.Add(new YdbStringAttributeConvention(Dependencies)); - coreConventions.Add(new YdbValueGenerationConvention(Dependencies, RelationalDependencies)); return coreConventions; } } diff --git a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs b/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs deleted file mode 100644 index 4790cbb9..00000000 --- a/src/EFCore.Ydb/src/Metadata/Conventions/YdbValueGenerationConvention.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Conventions; -using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Internal; - -namespace EntityFrameworkCore.Ydb.Metadata.Conventions; - -public class YdbValueGenerationConvention( - ProviderConventionSetBuilderDependencies dependencies, - RelationalConventionSetBuilderDependencies relationalDependencies) - : RelationalValueGenerationConvention(dependencies, relationalDependencies) -{ - protected override ValueGenerated? GetValueGenerated(IConventionProperty property) - { - var table = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault(); - - return !MappingStrategyAllowsValueGeneration(property, property.DeclaringType.GetMappingStrategy()) - ? null - : table.Name != null - ? GetValueGenerated(property, table) - : property.DeclaringType.IsMappedToJson() -#pragma warning disable EF1001 // Internal EF Core API usage. - && property.IsOrdinalKeyProperty() -#pragma warning restore EF1001 // Internal EF Core API usage. - && (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false - ? ValueGenerated.OnAddOrUpdate - : property.GetMappedStoreObjects(StoreObjectType.InsertStoredProcedure).Any() - ? GetValueGenerated((IReadOnlyProperty)property) - : null; - } - - private new static ValueGenerated? GetValueGenerated( - IReadOnlyProperty property, - in StoreObjectIdentifier storeObject - ) - { - var valueGenerated = GetValueGenerated(property); - return valueGenerated - ?? (property.GetComputedColumnSql(storeObject) != null - ? ValueGenerated.OnAddOrUpdate - : property.GetDefaultValueSql(storeObject) != null - ? ValueGenerated.OnAdd - : null); - } -} diff --git a/src/EFCore.Ydb/src/Migrations/Internal/YdbHistoryRepository.cs b/src/EFCore.Ydb/src/Migrations/Internal/YdbHistoryRepository.cs index a4de8c3e..f7b28dd1 100644 --- a/src/EFCore.Ydb/src/Migrations/Internal/YdbHistoryRepository.cs +++ b/src/EFCore.Ydb/src/Migrations/Internal/YdbHistoryRepository.cs @@ -45,7 +45,7 @@ public override async Task AcquireDatabaseLockAsync( { await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync( AcquireDatabaseLockCommand(), - ((IYdbRelationalConnection)Dependencies.Connection).Clone(), // TODO usage ExecutionContext + Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken @@ -85,9 +85,7 @@ private async Task ReleaseDatabaseLockAsync() try { await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync( - ReleaseDatabaseLockCommand(), - ((IYdbRelationalConnection)Dependencies.Connection).Clone() - ).ConfigureAwait(false); + ReleaseDatabaseLockCommand(), Dependencies.Connection).ConfigureAwait(false); return; } @@ -98,10 +96,8 @@ await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync( } } - private IReadOnlyList ReleaseDatabaseLockCommand() => - Dependencies.MigrationsSqlGenerator.Generate(new List - { new SqlOperation { Sql = GetDeleteScript(LockKey) } } - ); + private IReadOnlyList ReleaseDatabaseLockCommand() => Dependencies.MigrationsSqlGenerator + .Generate(new List { new SqlOperation { Sql = GetDeleteScript(LockKey) } }); bool IHistoryRepository.CreateIfNotExists() => CreateIfNotExistsAsync().GetAwaiter().GetResult(); @@ -135,13 +131,7 @@ await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync( private IReadOnlyList GetCreateIfNotExistsCommands() => Dependencies.MigrationsSqlGenerator.Generate(new List - { - new SqlOperation - { - Sql = GetCreateIfNotExistsScript(), - SuppressTransaction = true - } - }); + { new SqlOperation { Sql = GetCreateIfNotExistsScript(), SuppressTransaction = true } }); public override string GetCreateIfNotExistsScript() => GetCreateScript().Replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS"); @@ -179,8 +169,8 @@ private IReadOnlyList SelectHistoryTableCommand() => { new SqlOperation { - Sql = $"SELECT * FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)}" + - $" WHERE '{MigrationIdColumnName}' = '{LockKey}';" + Sql = $"SELECT * FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)} " + + $"WHERE '{MigrationIdColumnName}' = CAST(RandomUuid(0) AS Text)" } }); diff --git a/src/EFCore.Ydb/src/Migrations/YdbMigrationsSqlGenerator.cs b/src/EFCore.Ydb/src/Migrations/YdbMigrationsSqlGenerator.cs index 79c68fce..a3ed9bd1 100644 --- a/src/EFCore.Ydb/src/Migrations/YdbMigrationsSqlGenerator.cs +++ b/src/EFCore.Ydb/src/Migrations/YdbMigrationsSqlGenerator.cs @@ -81,6 +81,11 @@ MigrationCommandListBuilder builder .Append(" ") .Append(columnType) .Append(operation.IsNullable ? string.Empty : " NOT NULL"); + + if (autoincrement == true) + return; + + DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder); } protected override void CreateTablePrimaryKeyConstraint( diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs index 5cbb8500..61905efd 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs @@ -1,34 +1,21 @@ using System; -using Microsoft.EntityFrameworkCore.Storage; +using Ydb.Sdk.Ado.YdbType; namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; -// TODO: Await DateOnly support in Ydb.Sdk -public class YdbDateOnlyTypeMapping : RelationalTypeMapping +public class YdbDateOnlyTypeMapping : YdbTypeMapping { - private const string DateOnlyFormatConst = "{0:yyyy-MM-dd}"; - - public YdbDateOnlyTypeMapping(string storeType) - : base( - new RelationalTypeMappingParameters( - new CoreTypeMappingParameters(typeof(DateOnly)), - storeType, - StoreTypePostfix.None, - System.Data.DbType.Date - ) - ) + public YdbDateOnlyTypeMapping(YdbDbType ydbDbType) : base(typeof(DateOnly), ydbDbType) { } - protected YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) + private YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType) + : base(parameters, ydbDbType) { } - protected override YdbDateOnlyTypeMapping Clone(RelationalTypeMappingParameters parameters) => new(parameters); + protected override YdbDateOnlyTypeMapping Clone(RelationalTypeMappingParameters parameters) => + new(parameters, YdbDbType); - protected override string GenerateNonNullSqlLiteral(object value) - { - var dateOnly = (DateOnly)value; - return $"Date('{dateOnly.ToString(DateOnlyFormatConst)}')"; - } + protected override string SqlLiteralFormatString => $"{YdbDbType}('{{0:yyyy-MM-dd}}')"; } diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateTimeTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateTimeTypeMapping.cs index 47a37960..59f1ad2e 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateTimeTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateTimeTypeMapping.cs @@ -1,17 +1,27 @@ -using System.Data; -using Microsoft.EntityFrameworkCore.Storage; +using System; +using Ydb.Sdk.Ado.YdbType; namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; -public class YdbDateTimeTypeMapping( - string storeType, - DbType? dbType -) : DateTimeTypeMapping(storeType, dbType) +public class YdbDateTimeTypeMapping : YdbTypeMapping { - private const string DateTimeFormatConst = @"{0:yyyy-MM-dd HH\:mm\:ss.fffffff}"; + public YdbDateTimeTypeMapping(YdbDbType ydbDbType) : base(typeof(DateTime), ydbDbType) + { + } - private string StoreTypeLiteral { get; } = storeType; + private YdbDateTimeTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType) + : base(parameters, ydbDbType) + { + } - protected override string SqlLiteralFormatString - => "CAST('" + DateTimeFormatConst + $"' AS {StoreTypeLiteral})"; + protected override YdbDateTimeTypeMapping Clone(RelationalTypeMappingParameters parameters) => + new(parameters, YdbDbType); + + protected override string SqlLiteralFormatString => YdbDbType switch + { + YdbDbType.Timestamp or YdbDbType.Timestamp64 => $@"{YdbDbType}('{{0:yyyy-MM-ddTHH\:mm\:ss.ffffffZ}}')", + YdbDbType.Datetime or YdbDbType.Datetime64 => $@"{YdbDbType}('{{0:yyyy-MM-ddTHH\:mm\:ssZ}}')", + YdbDbType.Date or YdbDbType.Date32 => $"{YdbDbType}('{{0:yyyy-MM-dd}}')", + _ => throw new ArgumentOutOfRangeException(nameof(YdbDbType), YdbDbType, null) + }; } diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTimeSpanTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTimeSpanTypeMapping.cs new file mode 100644 index 00000000..de40022b --- /dev/null +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTimeSpanTypeMapping.cs @@ -0,0 +1,23 @@ +using System; +using System.Xml; +using Ydb.Sdk.Ado.YdbType; + +namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; + +public class YdbTimeSpanTypeMapping : YdbTypeMapping +{ + public YdbTimeSpanTypeMapping(YdbDbType ydbDbType) : base(typeof(TimeSpan), ydbDbType) + { + } + + private YdbTimeSpanTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType) + : base(parameters, ydbDbType) + { + } + + protected override YdbTimeSpanTypeMapping Clone(RelationalTypeMappingParameters parameters) => + new(parameters, YdbDbType); + + protected override string GenerateNonNullSqlLiteral(object value) => + $"{YdbDbType}('{XmlConvert.ToString((TimeSpan)value)}')"; +} diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs new file mode 100644 index 00000000..ae30758a --- /dev/null +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs @@ -0,0 +1,41 @@ +using System; +using System.Data.Common; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Json; +using Ydb.Sdk.Ado; +using Ydb.Sdk.Ado.YdbType; + +namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping; + +public abstract class YdbTypeMapping( + RelationalTypeMapping.RelationalTypeMappingParameters parameters, + YdbDbType ydbDbType +) : RelationalTypeMapping(parameters), IYdbTypeMapping +{ + public YdbDbType YdbDbType { get; } = ydbDbType; + + protected YdbTypeMapping( + Type clrType, + YdbDbType ydbDbType, + JsonValueReaderWriter? jsonValueReaderWriter = null + ) : this( + new RelationalTypeMappingParameters( + new CoreTypeMappingParameters(clrType, jsonValueReaderWriter: jsonValueReaderWriter), + ydbDbType.ToString() + ), ydbDbType + ) + { + } + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is not YdbParameter npgsqlParameter) + { + throw new InvalidOperationException( + $"Ydb-specific type mapping {GetType().Name} being used with non-Ydb parameter type {parameter.GetType().Name}"); + } + + base.ConfigureParameter(parameter); + npgsqlParameter.YdbDbType = YdbDbType; + } +} diff --git a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs index b01b160e..4d433711 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs @@ -38,19 +38,26 @@ RelationalTypeMappingSourceDependencies relationalDependencies private static readonly YdbDecimalTypeMapping Decimal = YdbDecimalTypeMapping.Default; - private static readonly GuidTypeMapping Guid = YdbGuidTypeMapping.Default; + private static readonly GuidTypeMapping Uuid = YdbGuidTypeMapping.Default; private static readonly YdbTextTypeMapping Text = YdbTextTypeMapping.Default; private static readonly YdbBytesTypeMapping Bytes = YdbBytesTypeMapping.Default; private static readonly YdbJsonTypeMapping Json = new("Json", typeof(JsonElement), null); - private static readonly YdbDateOnlyTypeMapping Date = new("Date"); - private static readonly DateTimeTypeMapping DateTime = new("DateTime"); + private static readonly YdbDateOnlyTypeMapping DateDateOnly = new(YdbDbType.Date); + private static readonly YdbDateOnlyTypeMapping Date32DateOnly = new(YdbDbType.Date32); - private static readonly YdbDateTimeTypeMapping Timestamp = new("Timestamp", DbType.DateTime); + private static readonly YdbDateTimeTypeMapping DateDateTime = new(YdbDbType.Date); + private static readonly YdbDateTimeTypeMapping Date32DateTime = new(YdbDbType.Date32); - // TODO: Await interval in Ydb.Sdk - private static readonly TimeSpanTypeMapping Interval = new("Interval", DbType.Object); + private static readonly YdbDateTimeTypeMapping Datetime = new(YdbDbType.Datetime); + private static readonly YdbDateTimeTypeMapping Datetime64 = new(YdbDbType.Datetime64); + + private static readonly YdbDateTimeTypeMapping Timestamp = new(YdbDbType.Timestamp); + private static readonly YdbDateTimeTypeMapping Timestamp64 = new(YdbDbType.Timestamp64); + + private static readonly YdbTimeSpanTypeMapping Interval = new(YdbDbType.Interval); + private static readonly YdbTimeSpanTypeMapping Interval64 = new(YdbDbType.Interval64); #endregion @@ -72,17 +79,20 @@ RelationalTypeMappingSourceDependencies relationalDependencies { "Float", [Float] }, { "Double", [Double] }, - { "Guid", [Guid] }, - - { "Date", [Date] }, - { "DateTime", [DateTime] }, - { "Timestamp", [Timestamp] }, - { "Interval", [Interval] }, + { "Guid", [Uuid] }, { "Text", [Text] }, { "Bytes", [Bytes] }, - { "Json", [Json] } + { "Date", [DateDateTime, DateDateOnly] }, + { "DateTime", [Datetime] }, + { "Timestamp", [Timestamp] }, + { "Interval", [Interval] }, + + { "Date32", [Date32DateTime, Date32DateOnly] }, + { "Datetime64", [Datetime64] }, + { "Timestamp64", [Timestamp64] }, + { "Interval64", [Interval64] } }; private static readonly Dictionary ClrTypeMapping = new() @@ -102,13 +112,13 @@ RelationalTypeMappingSourceDependencies relationalDependencies { typeof(float), Float }, { typeof(double), Double }, - { typeof(Guid), Guid }, + { typeof(Guid), Uuid }, { typeof(string), Text }, { typeof(byte[]), Bytes }, { typeof(JsonElement), Json }, - { typeof(DateOnly), Date }, + { typeof(DateOnly), DateDateOnly }, { typeof(DateTime), Timestamp }, { typeof(TimeSpan), Interval } }; diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs new file mode 100644 index 00000000..12449645 --- /dev/null +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs @@ -0,0 +1,168 @@ +using EntityFrameworkCore.Ydb.Extensions; +using EntityFrameworkCore.Ydb.FunctionalTests.TestUtilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Xunit; + +namespace EntityFrameworkCore.Ydb.FunctionalTests; + +public class InsertWithDefaultsTests +{ + [Fact] + public async Task Insert_WritesDefaultedNonNullColumns_Succeeds() + { + await using var testStore = YdbTestStoreFactory.Instance.Create("InsertWithDefaultsTests"); + + await using var dbContext = new TestEntityDbContext(); + await testStore.CleanAsync(dbContext); + + await dbContext.Database.MigrateAsync(); + + dbContext.Entities.AddRange(new TestEntity { Id = 1 }, new TestEntity { Id = 2 }, new TestEntity { Id = 3 }); + await dbContext.SaveChangesAsync(); + + foreach (var entity in dbContext.Entities.ToList()) + { + Assert.True(entity.BoolValue); + Assert.Equal(1, entity.Int8Value); + Assert.Equal(1, entity.Int16Value); + Assert.Equal(1, entity.Int32Value); + Assert.Equal(1, entity.Int64Value); + + Assert.Equal(1, entity.Uint8Value); + Assert.Equal(1, entity.Uint16Value); + Assert.Equal(1u, entity.Uint32Value); + Assert.Equal(1u, entity.Uint64Value); + + Assert.Equal(1, entity.FloatValue); + Assert.Equal(1, entity.DoubleValue); + Assert.Equal(1.00000m, entity.DecimalValue); + + Assert.Equal(Guid.Empty, entity.GuidValue); + + Assert.Equal("text", entity.TextValue); + Assert.Empty(entity.BytesValue); + + Assert.Equal(new DateOnly(1971, 12, 1), entity.DateValue); + Assert.Equal(new DateTime(1971, 12, 1, 0, 0, 0, DateTimeKind.Utc), entity.DateTimeValue); + Assert.Equal(TimeSpan.FromSeconds(1), entity.IntervalValue); + } + } + + public class TestEntityDbContext : DbContext + { + public DbSet Entities => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) => + modelBuilder.Entity(e => + { + e.ToTable("TestEntity"); + e.HasKey(x => x.Id); + e.Property(x => x.BoolValue).HasDefaultValue(true); + e.Property(x => x.Int8Value).HasDefaultValue(1); + e.Property(x => x.Int16Value).HasDefaultValue(1); + e.Property(x => x.Int32Value).HasDefaultValue(1); + e.Property(x => x.Int64Value).HasDefaultValue(1); + e.Property(x => x.Uint8Value).HasDefaultValue(1); + e.Property(x => x.Uint16Value).HasDefaultValue(1); + e.Property(x => x.Uint32Value).HasDefaultValue(1); + e.Property(x => x.Uint64Value).HasDefaultValue(1); + e.Property(x => x.FloatValue).HasDefaultValue(1); + e.Property(x => x.DoubleValue).HasDefaultValue(1); + e.Property(x => x.DecimalValue).HasDefaultValue(1); + e.Property(x => x.GuidValue).HasDefaultValue(Guid.Empty); + e.Property(x => x.TextValue).HasDefaultValue("text"); + e.Property(x => x.BytesValue).HasDefaultValue(Array.Empty()); + e.Property(x => x.DateValue).HasDefaultValue(new DateOnly(1971, 12, 1)); + e.Property(x => x.DateTimeValue).HasDefaultValue(new DateTime(1971, 12, 1, 0, 0, 0, DateTimeKind.Utc)); + e.Property(x => x.IntervalValue).HasDefaultValue(TimeSpan.FromSeconds(1)); + }); + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder + .UseYdb("Host=localhost;Port=2136", builder => builder + .DisableRetryOnFailure() + .MigrationsAssembly(typeof(TestEntityMigration).Assembly.FullName)) + .EnableServiceProviderCaching(false) + .LogTo(Console.WriteLine); + } + + public class TestEntity + { + public int Id { get; set; } + + public bool? BoolValue { get; set; } + + public sbyte Int8Value { get; set; } + public short Int16Value { get; set; } + public int Int32Value { get; set; } + public long Int64Value { get; set; } + + public byte Uint8Value { get; set; } + public ushort Uint16Value { get; set; } + public uint Uint32Value { get; set; } + public ulong Uint64Value { get; set; } + + public float FloatValue { get; set; } + public double DoubleValue { get; set; } + [Precision(25, 5)] public decimal DecimalValue { get; set; } + + public Guid GuidValue { get; set; } + + public string TextValue { get; set; } = null!; + public byte[] BytesValue { get; set; } = null!; + + public DateOnly DateValue { get; set; } + public DateTime DateTimeValue { get; set; } + public TimeSpan IntervalValue { get; set; } + } + + [DbContext(typeof(TestEntityDbContext))] + [Migration("InsertWithDefaultsTests_TestEntity")] + private class TestEntityMigration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) => + migrationBuilder.CreateTable( + name: "TestEntity", + columns: table => new + { + Id = table.Column(type: "Int32", nullable: false), + + BoolValue = table.Column(type: "Bool", nullable: false, defaultValue: true), + + Int8Value = table.Column(type: "Int8", nullable: false, defaultValue: (sbyte)1), + Int16Value = table.Column(type: "Int16", nullable: false, defaultValue: (short)1), + Int32Value = table.Column(type: "Int32", nullable: false, defaultValue: 1), + Int64Value = table.Column(type: "Int64", nullable: false, defaultValue: 1L), + + Uint8Value = table.Column(type: "Uint8", nullable: false, defaultValue: (byte)1), + Uint16Value = table.Column(type: "Uint16", nullable: false, defaultValue: (ushort)1), + Uint32Value = table.Column(type: "Uint32", nullable: false, defaultValue: (uint)1), + Uint64Value = table.Column(type: "Uint64", nullable: false, defaultValue: (ulong)1), + + FloatValue = table.Column(type: "Float", nullable: false, defaultValue: 1f), + DoubleValue = table.Column(type: "Double", nullable: false, defaultValue: 1d), + + DecimalValue = table.Column(type: "Decimal(25, 5)", precision: 25, scale: 5, + nullable: false, defaultValue: 1.0m), + + GuidValue = table.Column(type: "Uuid", nullable: false, defaultValue: Guid.Empty), + + TextValue = table.Column(type: "Text", nullable: false, defaultValue: "text"), + BytesValue = + table.Column(type: "Bytes", nullable: false, defaultValue: Array.Empty()), + + DateValue = table.Column(type: "Date", nullable: false, + defaultValue: new DateOnly(1971, 12, 1)), + DateTimeValue = table.Column(type: "Timestamp", nullable: false, + defaultValue: new DateTime(1971, 12, 1, 0, 0, 0, DateTimeKind.Utc)), + IntervalValue = table.Column(type: "Interval", nullable: false, + defaultValue: TimeSpan.FromSeconds(1)) + }, + constraints: table => { table.PrimaryKey("PK_TestEntity", x => x.Id); } + ); + + protected override void Down(MigrationBuilder migrationBuilder) => + migrationBuilder.DropTable(name: "TestEntity"); + } +} diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/SqlQueryCollectionParameterTests.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/SqlQueryCollectionParameterTests.cs index 0fe025e9..db088143 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/SqlQueryCollectionParameterTests.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/SqlQueryCollectionParameterTests.cs @@ -38,6 +38,8 @@ public static IEnumerable GetCollectionTestCases() yield return [(string[])["1", "2", "3"]]; yield return [new List { new byte[] { 1, 1 }, new byte[] { 2, 2 }, new byte[] { 3, 3 } }]; yield return [(byte[][])[[1, 1], [2, 2], [3, 3]]]; + yield return [new List { new(2002, 2, 24), new(2012, 2, 24), new(2102, 2, 24) }]; + yield return [new DateOnly[] { new(2002, 2, 24), new(2012, 2, 24), new(2102, 2, 24) }]; yield return [new List { SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3) }]; yield return [(DateTime[])[SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3)]]; @@ -71,6 +73,16 @@ public static IEnumerable GetCollectionTestCases() yield return [new List { new byte[] { 1, 1 }, new byte[] { 2, 2 }, new byte[] { 3, 3 }, null }]; yield return [(byte[]?[])[[1, 1], [2, 2], [3, 3], null]]; yield return + [ + new List + { new DateOnly(2002, 2, 24), new DateOnly(2012, 2, 24), new DateOnly(2102, 2, 24), null } + ]; + yield return + [ + new DateOnly?[] + { new DateOnly(2002, 2, 24), new DateOnly(2012, 2, 24), new DateOnly(2102, 2, 24), null } + ]; + yield return [ new List { SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3), null } ]; diff --git a/src/Ydb.Sdk/CHANGELOG.md b/src/Ydb.Sdk/CHANGELOG.md index c4745275..97f1151b 100644 --- a/src/Ydb.Sdk/CHANGELOG.md +++ b/src/Ydb.Sdk/CHANGELOG.md @@ -1,3 +1,5 @@ +- Feat ADO.NET: Added support for the `DateOnly` type in `YdbDataReader.GetFieldValue`. + ## v0.25.1 - Fixed bug ADO.NET: `ArgumentOutOfRangeException` when using `YdbParameter` with `YdbDbType = YdbDbType.List | YdbDbType.Unspecified`; diff --git a/src/Ydb.Sdk/src/Ado/YdbDataReader.cs b/src/Ydb.Sdk/src/Ado/YdbDataReader.cs index 0ebb94b6..be374ed4 100644 --- a/src/Ydb.Sdk/src/Ado/YdbDataReader.cs +++ b/src/Ydb.Sdk/src/Ado/YdbDataReader.cs @@ -342,6 +342,13 @@ public override T GetFieldValue(int ordinal) return (T)(object)GetChar(ordinal); } + if (typeof(T) == typeof(DateOnly)) + { + var dateTime = GetDateTime(ordinal); + + return (T)(object)DateOnly.FromDateTime(dateTime); + } + return base.GetFieldValue(ordinal); } diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs index fedb0637..ba881be3 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs @@ -140,10 +140,19 @@ public async Task Date_WhenSetDateOnly_ReturnDateTime() ydbCommand.Parameters.AddWithValue("dateOnly", new DateOnly(2002, 2, 24)); Assert.Equal(new DateTime(2002, 2, 24), await ydbCommand.ExecuteScalarAsync()); + var ydbDataReader = await ydbCommand.ExecuteReaderAsync(); + await ydbDataReader.ReadAsync(); + Assert.Equal(new DateOnly(2002, 2, 24), ydbDataReader.GetFieldValue(0)); + Assert.False(await ydbDataReader.ReadAsync()); ydbCommand.Parameters.Clear(); ydbCommand.Parameters.AddWithValue("dateOnly", DbType.Date, new DateOnly(2102, 2, 24)); Assert.Equal(new DateTime(2102, 2, 24), await ydbCommand.ExecuteScalarAsync()); + + ydbDataReader = await ydbCommand.ExecuteReaderAsync(); + await ydbDataReader.ReadAsync(); + Assert.Equal(new DateOnly(2102, 2, 24), ydbDataReader.GetFieldValue(0)); + Assert.False(await ydbDataReader.ReadAsync()); } [Theory] From 1e49098a1272e722cee430fd82b15eeb46920f17 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 11 Nov 2025 16:36:31 +0300 Subject: [PATCH 05/11] fix rename --- src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs index ae30758a..1c6de2c8 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs @@ -29,13 +29,13 @@ protected YdbTypeMapping( protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is not YdbParameter npgsqlParameter) + if (parameter is not YdbParameter ydbParameter) { throw new InvalidOperationException( $"Ydb-specific type mapping {GetType().Name} being used with non-Ydb parameter type {parameter.GetType().Name}"); } base.ConfigureParameter(parameter); - npgsqlParameter.YdbDbType = YdbDbType; + ydbParameter.YdbDbType = YdbDbType; } } From a93654034b39528dc3e26d57a49988ec3e96b0ce Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 11 Nov 2025 23:34:21 +0300 Subject: [PATCH 06/11] fix tests --- .../Query/GearsOfWarQueryYdbFixture.cs | 16 +++++++++----- .../Query/TPCGearsOfWarQueryYdbFixture.cs | 20 +++++++++-------- .../Query/TPTGearsOfWarQueryYdbFixture.cs | 22 ++++++++++--------- .../Query/TPTGearsOfWarQueryYdbTest.cs | 4 ++-- .../src/Ado/Internal/YdbValueExtensions.cs | 6 ++--- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/GearsOfWarQueryYdbFixture.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/GearsOfWarQueryYdbFixture.cs index 0dddba35..7643fe1a 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/GearsOfWarQueryYdbFixture.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/GearsOfWarQueryYdbFixture.cs @@ -20,14 +20,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity() .Property(e => e.Date) - .HasConversion( - v => v.ToDateTime(TimeOnly.MinValue), - v => DateOnly.FromDateTime(v)); + .HasColumnType("Date32"); modelBuilder.Entity() - .Property(e => e.Duration) + .Property(e => e.Time) + .HasColumnType("Datetime64") .HasConversion( - v => new DateTime(1970, 1, 1).Add(v), - v => v.TimeOfDay); + v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime + v => new TimeOnly(v.TimeOfDay.Ticks) + ); // DateTime → Time + + modelBuilder.Entity() + .Property(e => e.IssueDate) + .HasColumnType("Date32"); } } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs index c443d65c..6ae48d36 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs @@ -16,17 +16,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con base.OnModelCreating(modelBuilder, context); modelBuilder.Entity() - .Property(e => e.Time) - .HasConversion( - v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime - v => new TimeOnly(v.TimeOfDay.Ticks)) // DateTime → Time - .HasColumnType("DATETIME"); + .Property(e => e.Date) + .HasColumnType("Date32"); modelBuilder.Entity() - .Property(e => e.Duration) + .Property(e => e.Time) + .HasColumnType("Datetime64") .HasConversion( - v => new DateTime(2000, 1, 1).Add(v), // TimeSpan → DateTime - v => v.TimeOfDay) // DateTime → TimeSpan - .HasColumnType("DATETIME"); + v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime + v => new TimeOnly(v.TimeOfDay.Ticks) + ); // DateTime → Time + + modelBuilder.Entity() + .Property(e => e.IssueDate) + .HasColumnType("Date32"); } } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs index ce97a173..9a117180 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs @@ -6,7 +6,7 @@ namespace EntityFrameworkCore.Ydb.FunctionalTests.Query; -public class TptGearsOfWarQueryYdbFixture : TPTGearsOfWarQueryRelationalFixture +public class TPTGearsOfWarQueryYdbFixture : TPTGearsOfWarQueryRelationalFixture { protected override ITestStoreFactory TestStoreFactory => YdbTestStoreFactory.Instance; @@ -16,17 +16,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con base.OnModelCreating(modelBuilder, context); modelBuilder.Entity() - .Property(e => e.Time) - .HasConversion( - v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime - v => new TimeOnly(v.TimeOfDay.Ticks)) // DateTime → Time - .HasColumnType("DATETIME"); + .Property(e => e.Date) + .HasColumnType("Date32"); modelBuilder.Entity() - .Property(e => e.Date) + .Property(e => e.Time) + .HasColumnType("Datetime64") .HasConversion( - v => new DateTime(v.Year, v.Month, v.Day), // DateOnly → DateTime - v => new DateOnly(v.Year, v.Month, v.Day)) // DateTime → DateOnly - .HasColumnType("DATETIME"); + v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime + v => new TimeOnly(v.TimeOfDay.Ticks) + ); // DateTime → Time + + modelBuilder.Entity() + .Property(e => e.IssueDate) + .HasColumnType("Date32"); } } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbTest.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbTest.cs index 74b71e79..b66e3533 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbTest.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbTest.cs @@ -5,10 +5,10 @@ namespace EntityFrameworkCore.Ydb.FunctionalTests.Query; // TODO: Correlated subqueries right now are not supported in YDB -public class TptGearsOfWarQueryYdbTest : TPTGearsOfWarQueryRelationalTestBase +public class TPTGearsOfWarQueryYdbTest : TPTGearsOfWarQueryRelationalTestBase { // ReSharper disable once UnusedParameter.Local - public TptGearsOfWarQueryYdbTest(TptGearsOfWarQueryYdbFixture fixture, ITestOutputHelper testOutputHelper) + public TPTGearsOfWarQueryYdbTest(TPTGearsOfWarQueryYdbFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { Fixture.TestSqlLoggerFactory.Clear(); diff --git a/src/Ydb.Sdk/src/Ado/Internal/YdbValueExtensions.cs b/src/Ydb.Sdk/src/Ado/Internal/YdbValueExtensions.cs index 9ad5c1b5..ac305616 100644 --- a/src/Ydb.Sdk/src/Ado/Internal/YdbValueExtensions.cs +++ b/src/Ydb.Sdk/src/Ado/Internal/YdbValueExtensions.cs @@ -143,7 +143,7 @@ internal static Guid UnpackUuid(this Ydb.Value value) } internal static Ydb.Value PackDate(DateTime value) => - new() { Uint32Value = (uint)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerDay) }; + new() { Uint32Value = checked((uint)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerDay)) }; internal static DateTime UnpackDate(this Ydb.Value value) => UnixEpoch.AddTicks(value.Uint32Value * TimeSpan.TicksPerDay); @@ -155,7 +155,7 @@ internal static DateTime UnpackDate32(this Ydb.Value value) => UnixEpoch.AddTicks(value.Int32Value * TimeSpan.TicksPerDay); internal static Ydb.Value PackDatetime(DateTime value) => new() - { Uint32Value = (uint)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerSecond) }; + { Uint32Value = checked((uint)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerSecond)) }; internal static DateTime UnpackDatetime(this Ydb.Value value) => UnixEpoch.AddTicks(value.Uint32Value * TimeSpan.TicksPerSecond); @@ -167,7 +167,7 @@ internal static DateTime UnpackDatetime64(this Ydb.Value value) => UnixEpoch.AddTicks(value.Int64Value * TimeSpan.TicksPerSecond); internal static Ydb.Value PackTimestamp(DateTime value) => new() - { Uint64Value = (ulong)(value.Ticks - DateTime.UnixEpoch.Ticks) / TimeSpanUtils.TicksPerMicrosecond }; + { Uint64Value = checked((ulong)(value.Ticks - DateTime.UnixEpoch.Ticks) / TimeSpanUtils.TicksPerMicrosecond) }; internal static DateTime UnpackTimestamp(this Ydb.Value value) => UnixEpoch.AddTicks((long)(value.Uint64Value * TimeSpanUtils.TicksPerMicrosecond)); From ef1cfb40d2002d53d493f83a61c70e41d8b3d24a Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Wed, 12 Nov 2025 00:08:13 +0300 Subject: [PATCH 07/11] fix last tests --- .../LazyLoadProxyYdbTest.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs index 221c6d57..ac415698 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs @@ -7,12 +7,9 @@ namespace EntityFrameworkCore.Ydb.FunctionalTests; -public class LazyLoadProxyYdbTest : LazyLoadProxyTestBase +public class LazyLoadProxyYdbTest(LazyLoadProxyYdbTest.LoadYdbFixture fixture) + : LazyLoadProxyTestBase(fixture) { - public LazyLoadProxyYdbTest(LoadYdbFixture fixture) : base(fixture) - { - } - [ConditionalFact(Skip = "TODO: Fix precision")] public override void Can_serialize_proxies_to_JSON() => base.Can_serialize_proxies_to_JSON(); @@ -32,5 +29,18 @@ public TestSqlLoggerFactory TestSqlLoggerFactory protected override ITestStoreFactory TestStoreFactory => YdbTestStoreFactory.Instance; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity() + .Property(e => e.Birthday) + .HasColumnType("Timestamp64"); + + modelBuilder.Entity() + .Property(e => e.Birthday) + .HasColumnType("Timestamp64"); + } } } From 77cfa96585255abfcc494f6c5a9aa1e5fe0de911 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Wed, 12 Nov 2025 12:45:00 +0300 Subject: [PATCH 08/11] fix --- .github/workflows/tests.yml | 4 ++-- .../LazyLoadProxyYdbTest.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 429e3aab..4ecd0811 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -126,7 +126,7 @@ jobs: - name: Run EntityFrameworkCore.Ydb.QuickStart run: | cd ./examples/EntityFrameworkCore.Ydb.QuickStart - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.* dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate dotnet ef database update @@ -135,7 +135,7 @@ jobs: - name: Run EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial run: | cd ./examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.* dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate dotnet ef database update diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs index ac415698..07b3057d 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/LazyLoadProxyYdbTest.cs @@ -37,7 +37,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity() .Property(e => e.Birthday) .HasColumnType("Timestamp64"); - + modelBuilder.Entity() .Property(e => e.Birthday) .HasColumnType("Timestamp64"); From eed330fa11beabe747296e04b9571717bf48e82e Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Wed, 12 Nov 2025 14:28:48 +0300 Subject: [PATCH 09/11] chore fixes --- .../EntityFrameworkCore.Ydb.QuickStart.csproj | 2 +- examples/EntityFrameworkCore.Ydb.QuickStart/README.md | 2 +- .../AddEntity.Sample/AddEntity.Sample.csproj | 2 +- .../AddEntity.Sample/README.md | 2 +- .../Database.Operations.Tutorial.csproj | 2 +- .../Database.Operations.Tutorial/README.md | 2 +- .../Schema.ManyToMany/README.md | 2 +- .../Schema.ManyToMany/Schema.ManyToMany.csproj | 2 +- .../Schema.OneToMany/README.md | 2 +- .../Schema.OneToMany/Schema.OneToMany.csproj | 2 +- .../Schema.OneToOne/README.md | 2 +- .../Schema.OneToOne/Schema.OneToOne.csproj | 2 +- src/EFCore.Ydb/CHANGELOG.md | 4 +++- .../Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs | 2 +- .../InsertWithDefaultsTests.cs | 2 +- .../Query/TPCGearsOfWarQueryYdbFixture.cs | 2 +- .../Query/TPTGearsOfWarQueryYdbFixture.cs | 2 +- src/Ydb.Sdk/CHANGELOG.md | 1 + src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs | 8 ++++++++ 19 files changed, 28 insertions(+), 17 deletions(-) diff --git a/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj b/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj index 0622e0c7..f4bc20e2 100644 --- a/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj +++ b/examples/EntityFrameworkCore.Ydb.QuickStart/EntityFrameworkCore.Ydb.QuickStart.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/EntityFrameworkCore.Ydb.QuickStart/README.md b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md index 86b56ec9..d677ba07 100644 --- a/examples/EntityFrameworkCore.Ydb.QuickStart/README.md +++ b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md @@ -10,7 +10,7 @@ shows how to get started with EF, define a model, populate it with data and then 2. Create the database: ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate dotnet ef database update diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj index 5b482857..71d7a66c 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/AddEntity.Sample.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md index 4d600f18..2cbcf7b5 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md @@ -11,7 +11,7 @@ Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tut 2. Install the EF Core CLI tool and dependencies (if needed): ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj index e9e7ec20..1eabfe76 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/Database.Operations.Tutorial.csproj @@ -14,7 +14,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md index 45a23410..fb5a8b2d 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md @@ -10,7 +10,7 @@ Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tut 2. Install the EF Core CLI tool and dependencies (if needed): ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md index b12291c7..977fbdc6 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md @@ -11,7 +11,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj index 4449f9bb..10471c41 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/Schema.ManyToMany.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md index f1a9d02f..8c43d199 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md @@ -10,7 +10,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj index 29520a74..76057cce 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/Schema.OneToMany.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md index 5609e498..8bd68c5c 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md @@ -10,7 +10,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash - dotnet tool install --global dotnet-ef + dotnet tool install --global dotnet-ef --version 9.0.10 dotnet add package Microsoft.EntityFrameworkCore.Design ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj index 484e670b..f915940e 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/Schema.OneToOne.csproj @@ -13,7 +13,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/EFCore.Ydb/CHANGELOG.md b/src/EFCore.Ydb/CHANGELOG.md index 74748791..3eca7298 100644 --- a/src/EFCore.Ydb/CHANGELOG.md +++ b/src/EFCore.Ydb/CHANGELOG.md @@ -1,4 +1,6 @@ -- Fixed bug: Error when saving an entity without specifying a default value () +- Fixed bug: Missing/removed __EFMigrationsHistory caused INSERT errors (LockMigration); migrations now handle table recreation and locking properly ([#544](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/544)). +- Added support for new YDB data types - `Date32`, `Datetime64`, `Timestamp64` and `Interval64`. +- Fixed bug: Missing `DEFAULT` clause generation in `YdbMigrationsSqlGenerator` (handles defaultValue/defaultValueSql) ([#552](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/552)). - Fixed bug: SqlQuery throws exception when using list parameters ([#540](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/540)). - Added support for the YDB retry policy (ADO.NET) and new configuration methods in `YdbDbContextOptionsBuilder`: - `EnableRetryIdempotence()`: enables retries for errors classified as idempotent. You must ensure the operation itself is idempotent. diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs index 61905efd..da4c5785 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDateOnlyTypeMapping.cs @@ -9,7 +9,7 @@ public YdbDateOnlyTypeMapping(YdbDbType ydbDbType) : base(typeof(DateOnly), ydbD { } - private YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType) + private YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType) : base(parameters, ydbDbType) { } diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs index 12449645..32a38ddd 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/InsertWithDefaultsTests.cs @@ -108,7 +108,7 @@ public class TestEntity [Precision(25, 5)] public decimal DecimalValue { get; set; } public Guid GuidValue { get; set; } - + public string TextValue { get; set; } = null!; public byte[] BytesValue { get; set; } = null!; diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs index 6ae48d36..076710e0 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPCGearsOfWarQueryYdbFixture.cs @@ -26,7 +26,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime v => new TimeOnly(v.TimeOfDay.Ticks) ); // DateTime → Time - + modelBuilder.Entity() .Property(e => e.IssueDate) .HasColumnType("Date32"); diff --git a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs index 9a117180..27d6c319 100644 --- a/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs +++ b/src/EFCore.Ydb/test/EntityFrameworkCore.Ydb.FunctionalTests/Query/TPTGearsOfWarQueryYdbFixture.cs @@ -26,7 +26,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con v => new DateTime(2000, 1, 1).Add(v.ToTimeSpan()), // Time → DateTime v => new TimeOnly(v.TimeOfDay.Ticks) ); // DateTime → Time - + modelBuilder.Entity() .Property(e => e.IssueDate) .HasColumnType("Date32"); diff --git a/src/Ydb.Sdk/CHANGELOG.md b/src/Ydb.Sdk/CHANGELOG.md index 97f1151b..e3b1a4eb 100644 --- a/src/Ydb.Sdk/CHANGELOG.md +++ b/src/Ydb.Sdk/CHANGELOG.md @@ -1,3 +1,4 @@ +- Feat ADO.NET: Added overflow-checked int-to-uint cast for YDB `Date`, `Datetime`, and `Timestamp`. `DateTime` before epoch now throws `OverflowException`. - Feat ADO.NET: Added support for the `DateOnly` type in `YdbDataReader.GetFieldValue`. ## v0.25.1 diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs index ba881be3..5bf1ab5c 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs @@ -57,6 +57,14 @@ public void YdbParameter_WhenNoSupportedDbType_ThrowException(DbType dbType, str Assert.Equal("Ydb don't supported this DbType: " + name, Assert.Throws(() => new YdbParameter("$parameter", dbType) { IsNullable = true }.TypedValue).Message); + [Theory] + [InlineData(YdbDbType.Date)] + [InlineData(YdbDbType.Datetime)] + [InlineData(YdbDbType.Timestamp)] + public void YdbParameter_WhenDateTimeBeforeEpoch_ForDateDatetimeTimestamp_ThrowsOverflowException( + YdbDbType ydbDbType) => Assert.Throws(() => new YdbParameter("$parameter", ydbDbType) + { Value = new DateTime(1950, 1, 1) }.TypedValue); + [Fact] public void YdbParameter_WhenSetAndNoSet_ReturnValueOrException() { From 5882638c1c06b34b3c8dd30d26e75cbd8bac184d Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Wed, 12 Nov 2025 14:36:46 +0300 Subject: [PATCH 10/11] updates --- .github/workflows/tests.yml | 8 ++++---- examples/EntityFrameworkCore.Ydb.QuickStart/README.md | 2 +- .../AddEntity.Sample/README.md | 2 +- .../Database.Operations.Tutorial/README.md | 2 +- .../Schema.ManyToMany/README.md | 2 +- .../Schema.OneToMany/README.md | 2 +- .../Schema.OneToOne/README.md | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ecd0811..f7443b64 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -126,8 +126,8 @@ jobs: - name: Run EntityFrameworkCore.Ydb.QuickStart run: | cd ./examples/EntityFrameworkCore.Ydb.QuickStart - dotnet tool install --global dotnet-ef --version 9.* - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet tool install --global dotnet-ef --version 9.0.10 + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 dotnet ef migrations add InitialCreate dotnet ef database update dotnet run @@ -135,8 +135,8 @@ jobs: - name: Run EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial run: | cd ./examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial - dotnet tool install --global dotnet-ef --version 9.* - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet tool install --global dotnet-ef --version 9.0.10 + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 dotnet ef migrations add InitialCreate dotnet ef database update dotnet run diff --git a/examples/EntityFrameworkCore.Ydb.QuickStart/README.md b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md index d677ba07..511e723e 100644 --- a/examples/EntityFrameworkCore.Ydb.QuickStart/README.md +++ b/examples/EntityFrameworkCore.Ydb.QuickStart/README.md @@ -11,7 +11,7 @@ shows how to get started with EF, define a model, populate it with data and then 2. Create the database: ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 dotnet ef migrations add InitialCreate dotnet ef database update ``` diff --git a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md index 2cbcf7b5..f7796263 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/AddEntity.Sample/README.md @@ -12,7 +12,7 @@ Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tut 2. Install the EF Core CLI tool and dependencies (if needed): ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 ``` 3. Create the database and apply migrations: diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md index fb5a8b2d..42bceb3d 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Database.Operations.Tutorial/README.md @@ -11,7 +11,7 @@ Based on the [tutorial](https://www.csharptutorial.net/entity-framework-core-tut 2. Install the EF Core CLI tool and dependencies (if needed): ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 ``` 3. Create the database and apply migrations: diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md index 977fbdc6..71d5d18e 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.ManyToMany/README.md @@ -12,7 +12,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 ``` 3. Add a migration to generate the DDL (if not already present): diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md index 8c43d199..456df5c8 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToMany/README.md @@ -11,7 +11,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 ``` 3. Add a migration to generate the DDL (if not already present): diff --git a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md index 8bd68c5c..bf444ab8 100644 --- a/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md +++ b/examples/EntityFrameworkCore.Ydb.Samples/Schema.OneToOne/README.md @@ -11,7 +11,7 @@ The focus here is to show what database schema (DDL) will be generated by EF Cor 2. Install the EF Core CLI tool (if needed): ```bash dotnet tool install --global dotnet-ef --version 9.0.10 - dotnet add package Microsoft.EntityFrameworkCore.Design + dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.10 ``` 3. Add a migration to generate the DDL (if not already present): From 8d8359d8cb84bf6c0d3c8c55ed8ada8c5695a9f2 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Wed, 12 Nov 2025 14:48:24 +0300 Subject: [PATCH 11/11] fix linter --- src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs index 5bf1ab5c..73b28687 100644 --- a/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs +++ b/src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs @@ -63,7 +63,7 @@ public void YdbParameter_WhenNoSupportedDbType_ThrowException(DbType dbType, str [InlineData(YdbDbType.Timestamp)] public void YdbParameter_WhenDateTimeBeforeEpoch_ForDateDatetimeTimestamp_ThrowsOverflowException( YdbDbType ydbDbType) => Assert.Throws(() => new YdbParameter("$parameter", ydbDbType) - { Value = new DateTime(1950, 1, 1) }.TypedValue); + { Value = new DateTime(1950, 1, 1) }.TypedValue); [Fact] public void YdbParameter_WhenSetAndNoSet_ReturnValueOrException()